ETag本身不实现增量更新,而是配合浏览器缓存机制判断资源一致性;真正实现增量更新依赖内容哈希命名(如app.a1b2c3.js)+ ETag辅助校验,结合immutable与Cache-Control实现高效缓存。

在 Nginx 中,ETag 本身不直接实现“增量更新”,而是配合浏览器缓存机制,让客户端判断本地资源是否与服务端一致,从而决定是否复用缓存(304 Not Modified)或重新下载(200 OK)。真正实现静态资源“增量更新”效果的关键,是结合 ETag + 资源内容哈希命名(如 app.a1b2c3.js),让 Nginx 正确生成和校验 ETag。
确保 Nginx 启用并正确生成 ETag
Nginx 默认对静态文件开启 ETag(基于文件最后修改时间 mtime 和大小 size),但该方式在文件内容不变仅修改时间时会失效。更可靠的做法是启用基于内容的 ETag —— 这需要 Nginx 版本 ≥ 1.19.8 并开启 etag on;(默认已开),且不被其他指令覆盖:
- 确认没有配置
etag off;或add_header ETag "";等禁用/覆盖行为 - 静态文件需由 Nginx 直接提供(非 proxy_pass),否则 ETag 可能来自上游,不可控
- 若使用 gzip_static 或 brotli_static,Nginx 会对压缩后文件单独生成 ETag,行为正常
用内容哈希文件名替代 ETag 的局限性
单纯依赖 ETag 在频繁部署场景下有风险:同一文件名(如 main.js)内容变更后,若 mtime 更新不及时或 NFS 时间不同步,ETag 可能未变,导致浏览器误用旧缓存。因此工业实践普遍采用:
- 构建时将文件内容哈希嵌入文件名(如
main.f3a7e7.js) - HTML 中引用带哈希的路径(通过 Webpack/Vite 等工具自动处理)
- 此时即使 ETag 失效,因 URL 改变,浏览器必然发起新请求,天然规避缓存污染
ETag 在这种模式下退为辅助角色:对相同哈希文件,仍可利用 304 减少带宽;对 CDN 或中间代理,也增强一致性校验。
配合 Cache-Control 实现精准缓存策略
ETag 需与缓存头协同工作才能发挥效果。推荐在静态资源 location 块中设置:
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
add_header Last-Modified "";
etag on;
}-
immutable告诉浏览器:该资源在过期前绝不会变更,可跳过条件请求(即不发 If-None-Match)——但前提是文件名含哈希,否则慎用 - 显式清空
Last-Modified可避免与 ETag 冲突(两者选一即可,现代浏览器优先用 ETag) -
expires 1y与immutable结合,使浏览器长期强缓存,仅在 URL 变更时刷新
验证 ETag 是否生效
可通过 curl 或浏览器开发者工具检查响应头和请求流程:
- 首次请求:查看响应含
ETag: "abc123"和Cache-Control: public, immutable - 刷新页面:观察 Network 面板中该资源状态码是否为
200 (from memory cache)(因 immutable) - 手动发起条件请求:
curl -H "If-None-Match: \"abc123\"" https://example.com/app.js,应返回304 Not Modified - 修改文件后重新构建部署:新文件名对应新 ETag,旧 URL 不再被访问,自然实现“增量更新”语义
不复杂但容易忽略:ETag 是缓存协商机制,不是更新触发器;真正的增量更新靠构建时的文件指纹 + URL 变更驱动,ETag 只负责让这个过程更健壮、更省流量。


















