ETag不是缓存开关而是协商校验机制,仅在缓存过期、响应含ETag/Last-Modified、客户端带If-None-Match/If-Modified-Since且Nginx比对一致时触发304,需后端、Nginx配置与客户端行为协同生效。

开启 etag 本身不会自动“增强缓存”,它只在缓存过期后参与服务端再验证(revalidation),通过 304 Not Modified 响应减少重复传输,节省带宽和后端压力。是否生效,取决于后端响应头、Nginx 配置协同及客户端行为,不是简单开关就能起作用。
ETag 不是缓存开关,而是协商校验机制
ETag 是资源的唯一标识(如 "a1b2c3" 或 W/"x9y8z7"),仅在以下条件同时满足时触发 304:
- 浏览器本地有该资源缓存,但已过期(
max-age到期) - 原始响应中包含有效的
ETag或Last-Modified - 客户端发起新请求时带上
If-None-Match(对应 ETag)或If-Modified-Since(对应 Last-Modified) - Nginx 收到条件请求,并能比对出值未变
开启 etag on 的实际效果因场景而异
在不同资源类型下,开启 etag on 的价值差别很大:
- 静态文件(.js/.css/.png 等):Nginx 1.17.5+ 可基于内容生成强 ETag,修改文件内容后 ETag 自动变化,适合高频更新又需精准校验的构建产物(如 Webpack hash 文件)
-
动态接口(/api/xxx):Nginx 不会自动生成 ETag,除非后端明确返回
Last-Modified头——此时etag on才会派生弱 ETag;若后端直接返回ETag,Nginx 默认透传,无需开启 -
PHP/Python 等代理后端:若后端未设
Last-Modified或ETag,开启etag on无意义;强行启用还可能因 inode/mtime 不稳定导致误判 304
常见配置误区与风险点
很多看似“开了 ETag”却没生效,往往卡在这些细节:
- 同时使用
add_header ETag和etag on→ 后者被覆盖,ETag 值不一致,304 失败 - 启用了
gzip_static on或 Brotli 预压缩,但未配gzip_vary on→ 压缩版与未压缩版 ETag 冲突,协商失效 - CDN 或中间代理清除了
ETag或改写响应头 → 客户端收不到 ETag,自然无法发起If-None-Match - 后端返回
Cache-Control: no-store或must-revalidate缺失 → 浏览器跳过协商流程,直接回源取 200
真正提升验证效率的关键组合
单开 etag 效果有限,必须搭配以下配置才能稳定发挥价值:
- 对静态资源:用
expires 1y+add_header Cache-Control "public, immutable"→ 让浏览器长期复用,根本不过期,也就不需要 ETag - 对需频繁验证的接口:后端返回
Last-Modified,Nginx 配etag on+add_header Cache-Control "public, max-age=0, must-revalidate" - 防缓存击穿:加
proxy_cache_lock on,避免多个并发请求同时触发回源校验 - 降级保障:配
proxy_cache_use_stale updating+proxy_cache_background_update on,校验中仍返回旧缓存


















