缓存不是一个单一开关。一个响应既有一个由缓存作出的新鲜度决策,也有与源站之间的校验对话。把这两种想法混在一起,会得到陈旧内容或多余请求。
先选变更模型
对于发布之后可能变化的 HTML 文档,Cache-Control: no-cache 通常比 no-store 更接近正确起点。no-cache 允许存储,但要求缓存复用前先校验;no-store 则要求缓存完全不存储。当你想要条件请求与廉价的 304 Not Modified 时,这个区别很关键。
对类似 app.4f2c.js 这种带指纹的资源,URL 在字节变化时也随之改变,此时一个较长的新鲜度生命周期是合理的:
Cache-Control: public, max-age=31536000, immutable
但对一个内容可能被原地替换的稳定 URL,该策略不安全。如果 URL 稳定,应优先使用较短的生命周期,并搭配实体标签(ETag)或有意义的 Last-Modified 值。
同时检查两条路径
发一次普通请求,再发一次条件请求。各服务器的具体标志可能不同,但你想要的证据是稳定的:
curl -sS -D - -o /dev/null https://www.example.test/
curl -sS -D - -o /dev/null \
-H 'If-None-Match: "copy-a-real-etag-from-the-first-response"' \
https://www.example.test/
不要在 runbook 里粘贴编造的验证器。从第一次响应里复制该值,或使用测试夹具。只有服务器能证明所选表示未变化时,成功的重校验才应返回 304。
让中间层可见
Age、Vary 与 cache-control 指令说明了为什么两个客户端可能得到不同结果。如果响应随 Accept-Encoding、语言或其他请求字段变化,该维度应进入 Vary;否则共享缓存可能复用错误的表示。
有意义的发布检查不是"扫描器找到了缓存头",而是"该头与替换策略匹配,且条件请求的行为符合文档说明"。
来源
- RFC 9111 — HTTP Caching,第 4–5 节,已核验 2026-08-22。
- RFC 9110 — HTTP Semantics,第 8 与 13 节,已核验 2026-08-22。
- MDN:
Cache-Control,指令语义与默认值,已核验 2026-08-22。