html必须用etag而非last-modified,因后者精度仅秒级且易被构建工具误改mtime,导致无效刷新;etag基于内容哈希生成,内容不变则标识不变,保障协商准确。

HTML 不适合走强缓存,必须用 ETag 配合 no-cache 实现条件请求——否则用户刷新看到的可能是旧 HTML,连带加载错版本的 JS/CSS,直接白屏或样式崩溃。
为什么 HTML 必须用 ETag 而不是 Last-Modified
Last-Modified 基于文件修改时间,精度只有秒级,且容易被构建工具(如 touch、rsync)意外更新;哪怕 HTML 内容完全没变,只要 mtime 变了,服务端就会误判为“已更新”,返回 200 + 新内容,浪费带宽又破坏缓存复用。
ETag 用内容哈希(比如 md5(fileContent))生成才可靠:内容不变,ETag 就不变,哪怕文件重命名、部署时间戳重写、甚至跨服务器同步,协商结果依然准确。
常见错误现象:
- 本地开发用
file://打开 HTML,看不到任何ETag或协商行为(根本没 HTTP 头) - Nginx 默认的
etag on是基于 mtime + size,不抗 touch,线上要手动覆盖 - CDN 或反向代理(如 Cloudflare、Nginx upstream)清掉了原始
ETag,导致后续请求始终返回 200
Nginx 中正确启用 ETag 协商缓存
关键不是“加 ETag”,而是确保它由内容决定,并配合正确的 Cache-Control 指令触发协商流程。
推荐配置(放在匹配 HTML 的 location 块里):
location ~* \.html$ {
add_header Cache-Control "no-cache, must-revalidate";
add_header ETag "";
# 禁用 Nginx 默认的 mtime+size ETag
etag off;
# 若后端能输出 Content-MD5,也可用它生成 ETag(更可控)
}
说明:
-
add_header ETag ""让 Nginx 自动生成基于响应体的哈希值(需开启etag off避免冲突) -
no-cache明确告诉浏览器:别走强缓存,每次都要发条件请求;must-revalidate强制校验,绕过用户手动点击“强制刷新”的干扰 - 不要写
max-age=0—— 它语义模糊,有些旧客户端会降级成no-store行为 - 若用 Hugo/Jekyll 等静态生成器,可提前在构建时注入哈希到 HTML 文件名(如
index.a1b2c3.html),再配Cache-Control: immutable,彻底跳过协商——但前提是 HTML 真的不带动态数据
怎么验证 ETag 协商是否生效
别信地址栏回车或 F5,它们可能走内存缓存或跳过验证。真实验证只看 Network 面板里的 HTML 请求:
- Status 列显示
304 Not Modified→ 协商成功,浏览器用了本地 HTML 缓存 - Status 是
200 OK且 Response Headers 里有ETag→ 首次请求或内容真变了 - Status 是
200 from memory cache或200 from disk cache→ 强缓存生效了,Cache-Control配错了,HTML 被缓存了 - Request Headers 里没有
If-None-Match→ 浏览器根本没发协商请求,no-cache没生效,或者被中间层(CDN、代理)覆盖了响应头
额外检查点:打开 DevTools → Application → Cache Storage,确认里面没存 HTML(HTML 不该进 Cache API 或 HTTP Cache 的强缓存区)。
SPA 场景下 ETag 的特殊风险
单页应用的 HTML 通常是“壳”,真正逻辑在 JS 里;如果 HTML 本身用了 ETag 协商,但 JS/CSS 用了长缓存(max-age=31536000),就可能出现 HTML 是新版、JS 还是旧版的情况——页面初始化失败、路由报错、API 调用字段缺失。
解决思路不是禁用 ETag,而是保证版本一致性:
- 所有资源(HTML + JS + CSS + 字体)都用内容哈希命名,HTML 中引用的 JS 路径也带 hash(如
main.a1b2c3.js) - HTML 响应头设
Cache-Control: no-cache+ETag,JS/CSS 设Cache-Control: public, immutable, max-age=31536000 - 避免在 HTML 中内联关键 JS(比如初始化脚本),否则它的版本无法随文件名 hash 控制,只能靠 ETag 全量校验
最易被忽略的一点:很多 CI/CD 流程会在部署后自动 touch 所有 HTML 文件,这会让基于 mtime 的 ETag 失效——与其修复 ETag,不如直接切到内容哈希生成方式,或改用指纹化文件名 + immutable 策略。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











