缓存键、校验器与 Vary:HTTP 缓存放到 CDN 上要定的三件事
CDN 上的缓存问题常出在三个边界:缓存键包含哪些部分、过期后用什么校验器回源、哪些请求头通过 Vary 参与匹配。缓存键漏掉影响响应的参数会串内容,纳入无关参数会碎片化;ETag 生成不稳定会让每次发布都全量回源;Vary: Authorization 会让共享缓存几乎失效,带身份的内容应改用 private 或 no-store。
- 缓存键决定 CDN 命中哪份副本,默认实现通常不含请求头;影响响应的参数漏掉会串内容,无关参数纳入会降低命中率。
- ETag 要与内容标识绑定,用进程内随机值或启动时间派生会导致发布后回源量抬升。
- s-maxage 只作用于共享缓存,是区分浏览器与 CDN 时长的标准手段;带用户身份的响应不应靠 Vary 隔离。
把 HTTP 缓存放到 CDN 上,多数故障并不来自“没写 Cache-Control”,真正集中在三个边界:CDN 用什么键来索引对象、过期后靠什么校验回源、哪些请求头会改变响应内容。本文只讨论这三点,不展开响应分类与头部取舍。
缓存键:CDN 实际索引的对象是什么#
HTTP 规范没有规定缓存键的构造方式,由缓存实现自行决定。CDN 常见的默认键是 scheme、host、path 与 query string 的组合,请求头默认不参与,除非响应通过 Vary 声明。也就是说,URL 相同、仅请求头不同的两个请求,可能命中同一个副本。
由此产生两类方向相反的失误:一是键里漏掉影响响应的参数,/prices?currency=CNY 与 ?currency=USD 共用副本,后写入者覆盖前者;二是把无关参数计入键,utm_source 这类跟踪参数会让同一份内容在 CDN 上派生大量副本,命中率下降。
- 把决定响应内容的参数显式列出,作为缓存键白名单的依据。
- 在边缘层剔除无关参数属于 CDN 的配置能力,各厂商是否支持、语法如何,需查对应文档核实。
- 不兼容的变更走版本化路径(如
/v2/...),比依赖清除操作的时效更可预测。
GET /v1/prices?currency=CNY&utm_source=newsletter HTTP/1.1
Host: api.example.com
上面的域名与参数均为占位示例,未对真实服务发起请求。
过期之后:校验器决定回源成本#
存活期结束后,缓存若持有 ETag 或 Last-Modified,可以发起条件请求(If-None-Match / If-Modified-Since)。源站返回 304 表示内容未变,省掉响应体,但不省这次往返。
常见问题是 ETag 的生成方式不稳定:由进程内随机数、启动时间或部署序号派生,会导致每次发布或重启后所有副本失效,回源量瞬时抬升。建议让 ETag 与响应体的内容标识或数据版本绑定,使同一份内容在不同实例上得到相同值。
弱校验器 W/"..." 表示语义等价而非字节相同,适用于内容协商后仍可复用的场景;强校验器要求字节级一致。选哪种取决于下游能否容忍细微差异。
s-maxage 只对共享缓存生效,且优先于 max-age。需要区分浏览器与 CDN 的存活时长时,用它是标准做法。至于 stale-while-revalidate 这类扩展指令能否被某家 CDN 识别,同样待核实。
HTTP/1.1 200 OK
Cache-Control: public, max-age=60, s-maxage=600
ETag: "v42-prices-cny"
Vary: Accept-Encoding
Content-Type: application/json
以上响应头为示意,未在真实 CDN 或源站上验证。
Vary 与共享缓存的边界#
Vary 声明哪些请求头参与匹配。Vary: Accept-Encoding 常见且必要,否则压缩体与非压缩体可能串用。
风险在于放入基数过高的头。Vary: Authorization 会让每个令牌各自产生副本,共享缓存几乎失去意义;正确方向是让带用户身份的响应不被共享缓存存储(private 或 no-store),而不是靠 Vary 隔离。Vary: * 则表示该响应无法被复用。
另一个缺口是源站按 Cookie 或自定义头返回不同内容,却没有在响应中声明,共享缓存便按 URL 复用错误副本。这类问题的排查口径很简单:用同一 URL、不同请求头发送两次,比较响应体是否真的一致。
落地检查顺序#
- 列出所有影响响应内容的维度:query 参数、
Accept-Encoding、语言、鉴权状态。 - 为每个维度确定归属:进入缓存键、进入
Vary,还是根本不允许共享缓存。 - 确认 ETag 在重启与多实例下保持稳定。
- 把版本化路径作为发布主路径,清除操作只作兜底。
- 观察回源率与命中率的变化,而不是只看单次响应头。
边界说明#
本文未运行代码,也未在任何 CDN 上实测,内容属于协议语义与常见实现模式的说明,不构成对特定厂商默认缓存键、清除 API 或扩展指令支持情况的断言,这些需查阅对应厂商文档。缓存语义本身可参考 RFC 9111《HTTP Caching》与 RFC 9110《HTTP Semantics》原文(rfc9111、rfc9110);本文未联网核验其最新勘误。
"}