强缓存优先于协商缓存,Cache-Control优先级高于Expires;max-age=0属强缓存过期,no-cache强制验证,ETag验证精度高于Last-Modified。

强缓存优先于协商缓存,Cache-Control 优先于 Expires
浏览器判断是否用缓存,不是“看哪个头写了”,而是按明确的优先级链执行:先查 Cache-Control 是否允许强缓存;若不允许(如含 no-cache 或 max-age=0),才退到协商缓存阶段,发带 If-None-Match 或 If-Modified-Since 的请求去验证。
Expires 在现代实践中基本被忽略——只要响应里同时存在 Cache-Control 和 Expires,浏览器直接无视 Expires。它只在极老旧环境(HTTP/1.0 服务端)下兜底,且依赖客户端时间准确,偏差超 1 秒就可能让缓存提前失效或卡住不更新。
为什么 max-age=0 和 no-cache 表现相似但语义不同
两者都会导致浏览器不直接复用本地副本,而是发起条件请求(比如带 If-None-Match),但背后逻辑完全不同:
-
max-age=0属于强缓存过期,浏览器仍会把资源留在磁盘缓存里,只是下次访问时自动降级走协商流程 -
no-cache是明确指令“不许跳过验证”,哪怕缓存还新鲜,也必须和服务端确认有效性
推荐用 no-cache 而非 max-age=0 控制 HTML 缓存,语义更清晰,也避免因中间代理对 max-age 解析不一致引发问题。
立即学习“前端免费学习笔记(深入)”;
ETag 和 Last-Modified 的验证顺序与精度差异
当进入协商缓存阶段,服务器返回了 ETag 和 Last-Modified 两个头,浏览器默认优先使用 ETag 验证(即发 If-None-Match 请求)。因为:
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
-
ETag是内容哈希,只要文件内容变,标识就变,精度高 -
Last-Modified是秒级时间戳,同一秒内多次发布会导致服务器误判“没变”,返回304,用户看到旧 HTML
如果你的构建流程无法稳定生成 ETag(比如静态文件由 Nginx 自动计算,而它默认不启用 etag on),那协商缓存实际就退化成“几乎总 200”,失去意义。
meta http-equiv="Cache-Control" 基本无效,别把它当真
这个标签在 Chrome、Firefox、Edge 中完全不触发 HTTP 缓存控制逻辑,只对直接地址栏输入或书签打开的 HTML 文档有极弱影响;一旦页面被 fetch、htmx、iframe 加载,它就彻底失效。
真正起作用的只有服务器响应头里的 Cache-Control。Nginx 配置示例:
location = /index.html {
add_header Cache-Control "no-cache";
expires -1;
}
注意:expires -1 是为了显式关闭 Nginx 默认的 expires 指令干扰,确保最终响应头只含你定义的 Cache-Control。
最容易被绕开的点是:HTML 文件的缓存策略,和它引用的 JS/CSS 的缓存策略是解耦的。你给 index.html 设了 no-cache,但忘了给 main.a1b2c3.js 设 immutable,结果 HTML 更新了,JS 还是旧的——这种组合错误比单点配置错误更难排查。


















