用户登录

技术文章

外部链接将在新标签页打开。

HTTP 缓存与 CDN 加速实践:先定复用条件,再谈边缘加速

一句话回答

HTTP 缓存的核心是一个判定问题:响应能否被复用、由谁复用、复用多久、何时必须回源校验。Cache-Control 决定新鲜度与可存储范围,ETag/Last-Modified 负责过期后的校验,Vary 决定缓存键维度。CDN 边缘属于共享缓存,只负责执行这些语义;响应头写不清楚时,加速层只会把不确定性搬到离用户更近的地方。

HTTP 缓存要回答的是一个判定问题:这次响应能不能被复用、由谁复用、复用多久、什么时候必须回源校验。CDN 加速并不改变这个判定,只是把它放到离用户更近的一层执行。如果响应头没写清楚,换成更快的边缘节点也只是把不确定性前移。本文只讨论可核验的一般缓存语义;各家 CDN 产品的默认值、缓存键规则与调试字段名,本次未联网核验,需以对应文档为准。

缓存复用由两个机制分工#

按 HTTP 缓存的一般语义,复用分两步:先看新鲜度,不新鲜再走校验。

  • 新鲜度:Cache-Control: max-age=N 面向所有缓存,s-maxage 只约束共享缓存(CDN 边缘即属共享缓存)。Expires 是绝对时间,受时钟偏差影响,通常让位于 max-age。
  • 校验:过期后带 If-None-Match(配合 ETag)或 If-Modified-Since(配合 Last-Modified)回源,未变化则返回 304,不重传正文。

建议把两者看作互补而非二选一:带内容指纹的静态资源适合长 max-age 加 immutable;变化频繁但体积大的响应,更适合短新鲜度加 ETag 校验,用 304 省下正文传输。

关键响应头的分工#

响应头决定什么容易出错的地方
Cache-Control可存储范围与新鲜时长把 no-cache 当成 no-store:前者仍可存储,只是每次使用前必须校验
ETag内容是否变化的校验凭据按进程或节点生成,同一资源在不同边缘得到不同值,校验反复失效
Last-Modified基于时间的弱校验精度只到秒,短时间多次变更可能漏判
Vary缓存键要看哪些请求头维度缺失导致不同客户端拿到同一份内容;写成 * 则几乎等于不可复用
Age响应已在缓存中存放多久排查“为什么边缘返回的还是旧内容”时最直接的一列

CDN 是共享缓存,边界要单独声明#

推断而非规范强制的一点是:既然边缘是共享缓存,private、no-store 的响应就不应被边缘存储。实际是否严格执行取决于产品实现,需要用各家的调试响应头自行确认。

  • 带 Set-Cookie 或按用户区分的响应,不要给长 s-maxage;这类内容更适合留在浏览器缓存或回源。
  • Vary 的维度必须被边缘正确支持。若按 Accept-Encoding 分流,却未在缓存键中体现,就容易出现压缩内容与未压缩内容互相串。
  • 写入类请求默认不走缓存;若确实要给 GET 之外的方法做缓存,必须显式声明,并先确认语义是否允许。

一次可复现的检查步骤#

下面命令是示例,本次未在真实环境执行,仅说明观察顺序;域名使用占位地址。

curl -sSI https://www.example.com/static/app.js
  1. 看 Cache-Control:是否区分了 max-age 与 s-maxage,是否包含 private。
  2. 看 ETag:连续请求两次,值是否稳定。
  3. 看 Age:第二次请求是否递增,递增说明命中了中间缓存。
  4. 改一个 Vary 涉及的请求头(例如 Accept-Encoding)再请求,确认正文按预期分流。

部分 CDN 会附加自己的命中标记头,字段名与取值语义各家不同,本次材料未提供,需查对应文档后使用。

什么时候不该指望边缘缓存#

强个性化接口、鉴权后的内容、实时性要求高的读接口,通常不适合在边缘长期存储。即使命中率看起来很高,也要先确认缓存键没有被写得过粗——命中率虚高往往意味着把不该共享的响应共享了。缓存策略的收益应以回源请求量、带宽与响应时间等可测量的指标评估;本次没有测试数据,不给出具体数字。

把“能不能复用、谁能复用、复用多久、何时校验”写进响应头,CDN 才只是一个执行者,而不是一个猜测者。
||DSML|| parameter>

作者:沐小悠

本文约 4 分钟读完 · 关键词:HTTP 缓存、Cache-Control、ETag、Vary、CDN 加速

更多文章 GET 示例接口

「」标签下的其他文章