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 的关系 |
|---|---|---|
| Expires | HTTP/1.0 | 优先级低于 Cache-Control 中的 max-age 或 s-maxage;同时存在时 Cache-Control 生效 |
| Pragma | HTTP/1.0 | 仅用于请求头中的 Pragma: no-cache,作为向后兼容的降级方案 |
| ETag | HTTP/1.1 | 配合 if-None-Match 实现条件请求;Cache-Control 决定是否发送条件验证 |
| Last-Modified | HTTP/1.0/1.1 | 与 ETag 类似,提供时间戳验证;Cache-Control 可控制验证频率 |
| Vary | HTTP/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 在请求-响应链中的位置
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 练习题(自测理解)
- 当请求同时携带
Cache-Control: no-cache和Pragma: no-cache时,哪个指令被优先处理? - 如果响应头同时包含
Expires: Thu, 01 Dec 2025 16:00:00 GMT和Cache-Control: max-age=3600,浏览器应该以哪个为准?为什么? - CDN 代理节点收到包含
private指令的响应,它能否缓存该响应?请简述理由。