CDN 缓存键:一个决定命中率的隐藏开关
缓存键(cache key)是 CDN 判断“这个请求能不能命中缓存”的依据。很多人只设置缓存时长,却不管缓存键,结果要么命中率上不去,要么不同用户看到同一个页面却是错乱的内容。缓存键之所以重要,是因为它直接决定了同一份内容会被缓存成多少份、又能被多少个请求复用。这篇讲清楚缓存键由什么组成,以及怎么配置才能既提高命中率又不把内容搞混。
缓存键由什么组成
先看一个直观的例子:https://example.com/product/42?lang=zh&utm_source=weibo 和 https://example.com/product/42?lang=zh&utm_source=baidu,页面内容其实一模一样,只是来源参数不同。如果缓存键连 query 都算进去,这两次请求就会各自缓存一份,浪费空间还拉低命中率;如果只按“路径 + lang”算,它们就能共用同一个缓存副本。缓存键通常是几个维度的组合:
| 维度 | 是否该纳入 | 说明 |
|---|---|---|
| URL 路径 | ✅ 必须 | 不同页面必然不同 |
| Host | ✅ 必须 | 多域名时避免串站 |
| Query 参数 | ⚠️ 白名单 | 只保留影响内容的参数 |
| Cookie / 请求头 | ⚠️ 慎用 | 只对认证类接口使用 |
| 语言 | ✅ 按需 | 多语言站点必须区分 |
设计原则
- 静态资源按文件哈希做强缓存:
app.css发布后改名成app.a1b2c3.css,内容变了文件名就变,缓存键天然失效,可以放心设一年有效期; - HTML 页面按路径 + 语言区分:
/zh-cn/about和/en-us/about是不同内容,缓存键里必须带上语言前缀,否则英文用户会看到中文页面; - 对营销参数做白名单:
?utm_source=xxx&utm_campaign=yyy这类参数不影响页面内容,放进来只会把同一页拆成无数个缓存副本,命中率直线下降; - 登录态与购物车接口禁止缓存:这些接口因人而异,缓存了轻则串数据,重则泄露隐私。
怎么监控缓存命中率
缓存键配得对不对,最终都要看命中率数据。主流 CDN 后台都有命中率报表:Cloudflare 在 Caching 面板看 Cache Hit Ratio,阿里云 CDN 在监控里看命中率趋势。判断标准很简单——静态资源命中率应该在 90% 以上,动态页面可以低一些,但别长期低于 40%。如果命中率突然下降,先看是不是改过缓存键、加了新参数,或者源站响应头里的 Cache-Control 被意外改动;排查时按改动时间对一下发布记录,通常很快就能找到元凶。
缓存失效:改版后怎么让旧缓存尽快下线
缓存键决定命中,缓存失效决定“更新多快生效”。改版后最怕旧页面还留在边缘节点:要么等 TTL 自然过期,要么主动清理。两个实用手段:一是版本化资源,改 JS/CSS 就换文件名,新页面自然引用新文件;二是发布后用 CDN 的 purge 功能批量清理相关 URL。别在高峰时段全量刷新,否则会瞬间把回源流量打满,反而制造新的故障。养成“发布即清理、清理有范围”的习惯,比依赖 TTL 过期可靠得多。
配置示例:Cloudflare 与 Nginx
Cloudflare 里可以用 Cache Rules 控制缓存键:
{
"expression": "(http.host eq \"example.com\")",
"action": {
"type": "set_cache_settings",
"cache_key": {
"include_query": false,
"include": ["header:accept-language"]
}
}
}
Nginx 里用 proxy_cache_key 自定义缓存键:
location / {
proxy_cache_key "$host$request_uri";
# 静态资源按文件哈希做缓存键
location ~* \.(css|js|png|jpg)$ {
proxy_cache_key "$host$uri";
expires 1y;
}
}
两边的思路一致:动态页面把 $request_uri(含 query)排除出缓存键,静态资源直接按 URI 缓存一年。
一个真实场景:多语言电商站
假设你运营一个中英文电商站,商品页带 ?lang=zh&utm_source=weibo 这类参数。如果缓存键包含全部 query,那么 ?lang=zh&utm_source=weibo、?lang=zh&utm_source=baidu、?lang=en 都会各自缓存一份,同一商品可能被拆成几十个缓存副本。正确做法是:把 lang 纳入缓存键,把 utm_* 排除。改完之后命中率可能从 40% 涨到 90% 以上,回源压力大幅下降,页面打开速度也随之变快。下面是一组典型的改前改后对比:
| 配置 | 缓存副本数 | 命中率 | 回源压力 |
|---|---|---|---|
| 全部 query 入键 | 几十份/商品 | 约 40% | 高 |
| 只保留 lang | 2-3 份/商品 | 90%+ | 大幅下降 |
常见问题
- Query 参数全部参与缓存键导致命中率下降:解决方法是白名单,只保留真正影响内容的参数(比如
lang、page),其余一律剔除; - 多语言页面未区分语言导致内容错乱:在缓存键里显式加入语言维度,或在 URL 路径里带上语言前缀;
- 登录后看到别人的数据:认证类页面不要走公共缓存,用
Cache-Control: private或直接绕过 CDN; - 改了源站内容页面却没变:边缘节点还在 TTL 内的旧缓存里。先确认是不是缓存键没变导致命中旧副本,再决定等过期还是主动 purge。
参考:Cloudflare Cache Keys 文档 https://developers.cloudflare.com/cache/how-to/cache-keys/ ,Nginx proxy_cache_key 文档 https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cache_key