HTTP 缓存:Cache-Control 与 ETag 各管一半

Cache-Control 决定「要不要发请求」,ETag 决定「发出去之后能不能省下响应体」。把两者混谈是缓存问题的根源:改一个参数不会同时修好两侧。

缓存问题几乎都能拆成两个独立的问题:要不要发这个请求,和发了之后能不能省下响应体。前者由 Cache-Control 管,后者由 ETag / Last-Modified 管。

第一半:要不要发请求

Cache-Control: public, max-age=31536000, immutable

带哈希文件名的构建产物就该长这样:一年、immutable、直接不发请求。

Cache-Control: no-cache

no-cache 是最容易被误读的指令:它不是不缓存,而是「可以缓存,但每次必须回来验证」。真正禁止存储的是 no-store。

指令 浏览器存吗 会发请求吗
max-age=0 存 会,带验证
no-cache 存 会,带验证
no-store 不存 会,且不写盘
immutable 存 有效期内部不发

第二半:能不能省下响应体

服务端返回 ETag: "abc123",浏览器下次带上:

If-None-Match: "abc123"

没变就回 304 Not Modified,没有响应体。省的是传输,不是往返 —— 请求还是发了。

etag on;

组合起来看

目标 Cache-Control 验证器
带哈希的静态资源 max-age=31536000, immutable 不需要
HTML 页面 no-cache ETag
API 可缓存响应 max-age=60, must-revalidate ETag
敏感数据 no-store 无意义

HTML 通常选 no-cache + ETag:内容变了立刻生效,没变就一个 304,省下整个文档。

两个容易踩的点

改了 ETag 的生成方式会让所有缓存失效一次。 如果 ETag 由文件 mtime 派生,重新部署(即使内容没变)也会让全部客户端重新下载。内容哈希更稳。

CDN 会改写或吃掉缓存头。 源站写 max-age=60,CDN 可能按自己的规则缓存更久。排查「改了没生效」先看 CDN 的缓存状态头(eo-cache-status、cf-cache-status 之类),别只盯源站。

Cache-Control 管要不要问,ETag 管问了之后给不给内容。混在一起谈就永远修不好。

← 返回文章列表

评论

…