1. Cache-Control 概述:定义、作用域、与其他缓存头的关系

1.1 定义

Cache-Control 是 HTTP/1.1 协议中用于控制缓存行为的核心头部字段。它通过一组指令(directives)告诉缓存服务器、浏览器或代理节点:是否可以缓存响应、缓存多久、何时需要重新验证。指令采用键值对或关键词形式,例如 max-age=3600、no-cache、public。

核心定义:Cache-Control 是请求-响应链上所有缓存参与者共同遵循的“交通规则”,精确指令取代了早期 HTTP/1.0 中模糊的过期语义。

1.2 作用域

Cache-Control 可出现在请求头(Request Header)和响应头(Response Header)中,作用域覆盖所有参与缓存的节点:

作用方向典型场景示例指令
请求头浏览器强制代理或源站不返回缓存内容Cache-Control: no-cache
响应头源站告诉下游缓存资源可被存储多长时间Cache-Control: public, max-age=31536000
共享缓存层CDN、反向代理、网关等中间件s-maxage=86400
私有缓存层浏览器本地缓存private 或 no-store

1.3 与其他缓存头的关系

在 HTTP 缓存体系中,Cache-Control 并非孤岛,它与以下头部协同工作,形成优先级规则:

头部名称协议版本与 Cache-Control 的关系
ExpiresHTTP/1.0优先级低于 Cache-Control 中的 max-age 或 s-maxage;同时存在时 Cache-Control 生效
PragmaHTTP/1.0仅用于请求头中的 Pragma: no-cache,作为向后兼容的降级方案
ETagHTTP/1.1配合 if-None-Match 实现条件请求;Cache-Control 决定是否发送条件验证
Last-ModifiedHTTP/1.0/1.1与 ETag 类似,提供时间戳验证;Cache-Control 可控制验证频率
VaryHTTP/1.1指定缓存需根据哪些请求头区分缓存版本,Cache-Control 决定整体缓存时长

缓存系统的实际决策顺序如下:

1. 检查 Cache-Control 指令(首选)
   └─ 若有 max-age / s-maxage → 直接使用
   └─ 若有 no-cache / no-store → 跳过后续验证
2. 若没有 Cache-Control,则回退到 Expires
3. 若两者都没有,则使用启发式算法(Heuristic Freshness)
常见误区:no-cache 并不是“完全不缓存”,而是“每次使用前必须向源站验证是否可用”。真正禁止缓存的是 no-store。

1.4 类比理解:邮局寄信中的“缓存指令”

  • public → 像一份传单,邮局可以复印并分发给所有人。
  • private → 像一封私人信件,只有收件人本人才能保留副本。
  • max-age=3600 → 信封上写明“1小时内有效”,过期后邮局必须重新问寄信人要新版本。
  • no-cache → 每次派送前,邮递员都必须打电话给寄件人确认内容是否仍正确。
  • no-store → 信件阅后即焚,邮局和收件人都不能留底。

1.5 原理图解:Cache-Control 在请求-响应链中的位置

Cache-Control 作用域与传递流程 🌐 浏览器 私有缓存 🖥 代理/CDN 共享缓存 🗄 源站 原始响应 请求指令 no-cache / no-store 响应指令 public, max-age=3600 🔁 缓存决策点 代理/CDN 根据 Cache-Control 判断: 是否存储响应?是否需要向源站重新验证? 无法协商时使用 Cache-Control: must-revalidate 强制验证

1.6 应用边界与注意事项

  • 仅控制 HTTP 缓存层:Cache-Control 不干预浏览器 localStorage、Service Worker 或 AppCache 等内部存储机制。
  • 共享缓存 vs 私有缓存:public 允许任何节点缓存,private 仅允许最终客户端缓存。
  • 与 CDN 配置的协同:许多 CDN 会额外使用自身缓存规则(如 TTL 覆盖),Cache-Control 仍是源站控制的首选标准。
  • 启发式缓存:当没有 Cache-Control 和 Expires 时,浏览器可能根据 Last-Modified 或 Date 自动估算新鲜度。
常见误区:认为 Cache-Control: max-age=0 等同于 no-cache,实际上两者语义不同:max-age=0 表示“已经过期,可以验证”,而 no-cache 强调“每次必须验证”。实践中大部分浏览器会将两者等同处理,但 RFC 建议优先使用 no-cache 明确表达验证需求。

1.7 练习题(自测理解)

  1. 当请求同时携带 Cache-Control: no-cache 和 Pragma: no-cache 时,哪个指令被优先处理?
  2. 如果响应头同时包含 Expires: Thu, 01 Dec 2025 16:00:00 GMT 和 Cache-Control: max-age=3600,浏览器应该以哪个为准?为什么?
  3. CDN 代理节点收到包含 private 指令的响应,它能否缓存该响应?请简述理由。