HTML必须禁用强缓存、仅启用协商缓存,因其是页面入口,若被强缓存(如max-age=3600),浏览器一小时内不发请求,旧HTML仍引用旧JS/CSS路径,导致404或逻辑错乱;no-cache强制每次协商验证,确保结构最新。

HTML 文件必须禁用强缓存,只开协商缓存;否则用户永远看不到新版本页面——哪怕你 JS 文件名带 hash 也没用。
为什么 HTML 不能配 max-age
HTML 是整个页面的入口,浏览器加载它之后才去拉 JS/CSS。如果 HTML 被强缓存了(比如 Cache-Control: max-age=3600),那接下来一小时内,用户看到的永远是旧 HTML 结构:哪怕你新部署了带 main.a1b2c3.js 的构建产物,旧 HTML 里写的还是 main.x9y8z7.js,资源 404 或逻辑错乱就不可避免。
- 强缓存命中时,浏览器连请求都不发,
200 (from memory cache)状态码下根本不会触达服务器 -
no-cache不是“不缓存”,而是强制每次走协商流程——这才是 HTML 正确姿势 - Vite、Next.js 等现代框架默认对
index.html返回Cache-Control: no-cache,不是巧合,是必须
Cache-Control: no-cache 和 max-age=0 怎么选
两者效果接近,但语义和行为有细微差别:
-
Cache-Control: no-cache明确告诉浏览器:“可以存,但每次用前必须和服务端确认”——它触发协商缓存,要求服务端返回ETag或Last-Modified -
Cache-Control: max-age=0表示“缓存立即过期”,也进入协商阶段,但部分老旧代理或中间件可能对其支持不一致 - 如果同时存在
Pragma: no-cache(HTTP/1.0 遗留头),它会覆盖Cache-Control,导致意外行为,务必检查响应头是否被反向代理(如 Nginx)悄悄加了这个头
协商缓存该用 ETag 还是 Last-Modified
ETag 是首选,Last-Modified 只作兜底:
立即学习“前端免费学习笔记(深入)”;
-
ETag是内容指纹(比如基于文件 hash 生成),只要内容变,ETag必变;而Last-Modified只精确到秒,同一秒内多次改文件,时间戳可能不变,导致协商误判为“未更新” - 多实例部署时,若各服务器用本地
mtime或inode生成ETag,不同机器返回的值可能不一致,协商失败——必须统一用文件内容 hash(如sha256(file))生成 - 静态资源(JS/CSS/图片)可配强缓存 +
ETag,比如Cache-Control: public, max-age=31536000,既省请求,又保更新安全
容易被忽略的缓存链路断点
你写了正确的 Cache-Control,但用户没生效,大概率是某一层把它吃掉了:
- CDN 默认不透传自定义响应头,需显式开启
Cache-Control透传,否则浏览器收到的是 CDN 自己加的头 - Nginx 反向代理若配置了
expires off或add_header Cache-Control "no-cache",会直接覆盖上游服务返回的头 - Service Worker 拦截请求后,若没正确
event.respondWith(fetch(event.request)),可能绕过 HTTP 缓存机制,造成行为不一致
查问题时,别只看代码里写的那一行,用 Chrome DevTools 的 Network 面板点开响应头,逐层比对:浏览器收到的、CDN 返回的、源站吐出的——三者必须一致,缓存策略才算真正落地。



















