HTTP 缓存语义
HTTP 缓存通过响应头表达可缓存性与过期策略,让浏览器、CDN 与网关在不回源的情况下返回内容。Cache-Control 决定缓存位置与有效期,ETag 与 Last-Modified 支持条件请求,命中校验时服务端返回 304,客户端复用本地副本,只付一次往返的代价。
缓存键为什么重要
缓存键由请求方法、路径以及 Vary 指定的头部共同决定。键未覆盖的维度一旦变化就会返回错误内容,典型事故是忽略了语言、编码或租户维度的差异,导致用户看到错误的语言版本;更严重的是带鉴权的响应被共享缓存存储,造成跨用户数据泄露。
常用响应头
常用响应头
Cache-Control: public, max-age=3600, stale-while-revalidate=60
ETag: "v7"
Vary: Accept-Encoding, Accept-Language
# public 允许共享缓存;private 只允许浏览器缓存
# no-cache 每次都要校验;no-store 完全禁止存储基本信息
| 强缓存 | 由 max-age 与 s-maxage 决定有效期,期间不回源 |
|---|---|
| 协商缓存 | ETag 与条件请求配合,未变更返回 304 |
| 共享与私有 | public 允许共享缓存,private 仅限浏览器 |
| 失效控制 | no-store 禁止存储,no-cache 要求每次校验 |
典型使用场景
- 静态资源:文件名带内容指纹,配长时间缓存
- 接口响应:对变化不频繁的查询设置短缓存
- CDN 边缘缓存:就近返回内容,降低回源带宽
- 条件请求:大文件断点续传与增量更新
- 灰度与回滚:通过版本化路径绕过旧缓存
怎么验证缓存生效
看响应头里的缓存命中和回源统计,而不是靠响应时间猜测。首次请求应为未命中并回源,第二次请求应命中缓存或返回 304;如果两次都回源,先检查请求是否携带了绕过缓存的头部、缓存键是否被额外维度打散,以及响应是否被声明为禁止存储。上线前用灰度和带指纹的资源路径验证,可以避免缓存错误内容被大面积分发。
带鉴权的响应默认不应被共享缓存存储:显式配置为私有或禁止存储,否则一旦被边缘节点缓存,其他用户可能拿到不属于自己的数据。