缓存头是和浏览器的一份双向契约
源站定义存储和新鲜度策略,缓存判断已保存的响应是否能满足下一次请求。无法直接复用时,验证器可以降低检查成本。这是几个相互独立的决定:在一次成功响应中找到 Cache-Control 和 ETag,并不能证明下一次请求会按预期执行。验收必须沿着重复请求继续观察,才能区分直接命中、成功验证和完整替换。 允许谁复用、可复用多久 private 把存储范围限制到单用户缓存,public 在其他条件允许时明确授予缓存许可。max-age 提供新鲜度寿命,s-maxage 则为共享缓存覆盖这个值。no-cache 允许保存,但要求每次复用前成功验证;no-store 禁止 HTTP 缓存存储,并不管住所有应用日志或用户保存的文件。这些指令都不能替代授权检查和传输安全,不能因为缓存头正确就省略访问控制。 对于带内容指纹的资源,如果字节变化一定带来新网址,public, max-age=31536000, immutable 可以是合理策略。这里的时长是一年,不是一个月。稳定网址下会变化的文档,应按更新要求选择策略:短寿命搭配验证器,允许有界的直接复用;no-cache 则要求后续每次 HTTP 缓存复用都先验证。两者的网络请求成本和可见更新延迟并不一样。 验证器回答另一个问题 客户端可以在 If-None-Match 中带回 ETag。条件 GET 的标签匹配时得到不含正文的 304;标签不匹配时,通常得到包含当前表示的 200。If-None-Match 使用弱比较,因此弱标签同样可以用于缓存验证。Last-Modified 支持 If-Modified-Since,但秒级时间戳可能遗漏快速连续变化。请求中只要存在 If-None-Match,就忽略 If-Modified-Since,而不是等标签匹配失败后再用日期兜底。 收到适用的 304 后,缓存保留原有正文,并依照缓存规则更新元数据。对于对应的 200 本来会发送的 Date、ETag、Cache-Control 等字段,源站在 304 中也有相应发送要求。一个只负责下载响应的程序,不会因此自动具备完整缓存能力;如果把 304 的空内容直接当作新的文档覆盖旧正文,反而会丢掉真正要使用的内容。 ...