HTTP 缓存与 CDN 加速:先分清谁能缓存,再定 TTL 与失效
HTTP 缓存与 CDN 加速的关键,是先判断响应能否进入共享缓存,再用 Cache-Control 区分私有与共享新鲜期,用 ETag 等校验器减少重复传输,并让 Vary 与缓存键保持一致;静态资源优先用文件名哈希失效,动态接口默认不共享缓存再逐条放开。
- 共享缓存会把同一份响应复用给不同用户,个性化响应必须先排除在共享缓存外。
- no-cache 表示使用前必须校验,不等于不缓存;不存储要用 no-store。
- 失效优先用文件名带内容哈希,主动 purge 的生效时间取决于服务商,需核实。
HTTP 缓存与 CDN 加速,关键不在把 TTL 调大,而在先回答一个问题:这个响应能不能被共享缓存保存并复用给其他用户?分类错了,后面调什么参数都会出问题。字段语义以 IETF 的 HTTP 缓存标准 RFC 9111 为准;本次未联网核验各 CDN 厂商的具体实现,涉及厂商差异的部分在文中单独标出。
先分清缓存所在的位置#
- 浏览器私有缓存:只服务单个用户,保存个性化响应通常安全。
- CDN 边缘共享缓存:一份副本可能返回给多个用户,只要响应因人而异,就必须阻止它进入共享缓存。
- 源站与回源链路:回源频率由边缘缓存的新鲜期决定,源站压力问题往往要在这一层解决。
Cache-Control 的几个决定性字段#
| 字段 | 作用 | 常见误用 |
|---|---|---|
max-age | 响应新鲜期,私有与共享缓存都适用 | 给带登录态的接口设长新鲜期 |
s-maxage | 只对共享缓存生效,覆盖 max-age | 以为设了它就同时控制浏览器 |
private / public | 声明是否允许共享缓存存储 | 不写,交给中间层默认策略 |
no-cache | 可以存储,但每次使用前必须向源站校验 | 当成“完全不缓存” |
no-store | 不允许存储 | 与 no-cache 混用 |
stale-while-revalidate | 过期后先返回旧副本、后台再校验 | 默认所有 CDN 都支持(需核实) |
实践上,公开静态资源用较长的 max-age;接口按“能否共享”二分:能共享的写 public, s-maxage=…,不能共享的直接 private, no-store,不要留空让中间层替你决定。
校验器:ETag 与 Last-Modified#
新鲜期过后,缓存可以带上 If-None-Match 或 If-Modified-Since 回源校验,源站返回 304 即可省掉响应体。ETag 需要与内容真实对应:若源站按多台机器各自生成、或随响应变化而抖动,校验就退化为每次全量传输。压缩、转码链路是否会改写 ETag,取决于具体实现,需按实际链路核对。
Vary 与缓存键必须一致#
Vary 声明“哪些请求头会影响响应”,常见值是 Accept-Encoding、Accept-Language。它必须同时体现在缓存键里,否则会出现缓存命中错版本的问题;Vary: * 实际上会让响应难以被复用。CDN 的默认缓存键通常由主机名、路径和查询串组成,但组成方式与是否忽略指定参数由厂商配置决定,待核实。查询参数顺序不同会形成不同缓存键,接口层应做参数规范化。
失效的两条路径#
推荐静态资源走“文件名带内容哈希 + 长新鲜期”,发布即换 URL,不依赖主动失效。主动 purge 适合配置、文案这类无法改名的资源,但其生效时间与是否同步返回结果因厂商而异,不能假设调用成功即全网生效。回滚时要注意:旧文件名若已被清理,回滚需重新发布而非仅切回引用。
示意配置(未执行示例)#
以下片段仅示意头部写法,未在真实环境执行,端口、路径与中间件版本需按项目调整。
// 示意:Node.js / Express 风格,未执行
app.get('/api/public-config', (req, res) => {
res.set('Cache-Control', 'public, s-maxage=60, stale-while-revalidate=30');
res.json(config);
});
app.get('/api/me', (req, res) => {
res.set('Cache-Control', 'private, no-store');
res.json(profile);
});
# 示意:Nginx 静态资源,未执行
location ~* \.(?:js|css|woff2|png)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
}
上线前核对清单#
- 逐个接口标注“可共享 / 不可共享”,并检查响应头是否与该判断一致。
- 确认
no-cache与no-store没有被混用。 - 核对
Vary是否进入缓存键,压缩与多语言是否命中正确版本。 - 核对缓存键是否包含全部影响响应的查询参数,并做顺序规范化。
- 确认主动 purge 的生效范围与延迟,准备好不依赖 purge 的发布方式。
- 用带身份的请求与匿名请求各验证一次,确认不会互相串用缓存。
边界与待核实项#
各 CDN 对 s-maxage、stale-while-revalidate、immutable 的支持程度,以及 purge 的生效时间、默认缓存键组成,均需查阅对应厂商文档确认,本次材料不足以给出结论。涉及登录态、灰度分流、AB 实验的接口,建议默认按不可共享处理,再逐条放开并复核,而不是先全量开启缓存再排查。