CDN 缓存命中率优化技巧:提升 CDN 加速效果的实战策略

缓存命中率是 CDN 成本与体验的"双重仪表盘":命中率越高,回源请求越少,源站压力越小,用户拿到内容的延迟越低,账单上的回源流量费也越少。很多站长只盯着"有没有开 CDN",却从不看命中率,结果加速效果平平、费用却不低。这篇文章把能立刻上手的技巧按优先级过一遍。

一、先理解两个命中率

  • 字节命中率 (Byte Hit Ratio):CDN 直接响应的流量占比,一般应 > 85%
  • 请求命中率 (Request Hit Ratio):CDN 直接处理的请求占比,一般应 > 90%

两者不同:HTML 页面请求少但字节小,图片请求多、字节大。一个站如果字节命中率低,多半是动态内容或未缓存的接口在拖后腿。所以排查时两个指标要一起看:请求命中率低多半是动态接口,字节命中率低多半是图片或视频没缓存好。

常见内容的理想命中率

内容类型 理想命中率 说明
图片 > 95% 基本不变
CSS/JS > 95% 版本更新时变化
字体文件 > 99% 几乎不变
HTML 页面 50-80% 取决于变化频率
API 响应 0-50% 取决于可缓存性

二、八个能落地的优化技巧

技巧 1:按资源类型设置 TTL

不要一刀切。图片给 30 天 + immutable,带版本号的 CSS/JS 给一年,HTML 只给 1 小时:

# 图片 - 长时间缓存
location ~* \.(jpg|jpeg|png|gif|ico|svg)$ {
    expires 30d;
    add_header Cache-Control "public, immutable";
}

# CSS/JS - 带版本号
location ~* \.(css|js)$ {
    expires 365d;
    add_header Cache-Control "public, immutable";
}

# HTML - 短缓存
location ~* \.html$ {
    expires 1h;
    add_header Cache-Control "public, must-revalidate";
}

TTL 之外,s-maxagestale-if-error 这类指令也可以组合使用:前者控制共享缓存(CDN)的时长,后者让源站挂了还能继续吐旧内容,容错性更好。

技巧 2:文件版本化,而不是查询参数

style.css?v=1 这种"查询参数版本号"在很多 CDN 眼里是不同 URL,等于每次发布都要回源;把哈希写进文件名才是标准做法,还能配合 immutable 缓存一年:

# ❌ 不推荐
style.css?v=1

# ✅ 推荐
style.a1b2c3d4.css
main.55e8c9f1.js

现代构建工具(Vite、Webpack、Next.js)默认就产出带哈希的文件名,直接在构建配置里打开就行。配合 Service Worker 或构建产物的 preload,版本化文件还能进一步做骨架缓存,但这属于锦上添花,先把哈希文件名做对。

技巧 3:去掉 URL 里的动态参数

带 Session ID、随机 token 的 URL 会制造海量"假唯一"的缓存条目,把命中率拉低。能用 ignore query string 的 CDN 功能就开,命中率往往立竿见影。但注意:如果参数真的影响响应内容(比如分页),别盲目忽略,先改成规范的分页路径再说。

技巧 4:清理响应里的 Cookie

Set-Cookie 会直接阻断 CDN 缓存。静态资源上把 Cookie 清掉,动态页再交给专门策略:

# 静态资源清除 Cookie
location ~* \.(jpg|css|js)$ {
    add_header Cache-Control "public";
    add_header Set-Cookie "";
}

技巧 5:预取热门内容

CDN 预取(Pre-fetch)在空闲时把热门 MISS 内容主动拉到边缘节点,下次用户访问直接命中:

CDN 预取策略:
1. 分析缓存 MISS 请求
2. 识别热门 MISS 内容
3. 主动拉取到边缘节点
4. 减少下次 MISS

技巧 6:分层缓存

边缘节点 TTL 短、更新快;中层节点 TTL 长、容量大。回源只发生在中层,源站压力直接降一个数量级:

用户 → 边缘节点 (100ms TTL) → 中层节点 (1h TTL) → 源站

技巧 7:缓存 POST 响应

对"同参数同结果"的查询类 POST,部分 CDN 支持按请求体缓存响应(需确认支持),能显著降回源:

Cache-Control: public, max-age=3600
CDN-Cache: POST requests with same body

技巧 8:用日志找问题资源

定期拉 HIT/MISS 分布,把 MISS 占比最高的资源按字节排序,优先处理"又大又 MISS"的图片和 JS。

两个进阶玩法值得单独说。一是 stale-while-revalidate:让 CDN 先返回旧缓存、后台悄悄刷新,用户永远等不到回源的那一下,适合新闻、榜单这类内容;二是 Vary 头:同一 URL 根据设备类型返回不同版本时,Vary: User-Agent 会拆出多份缓存,用错了反而拉低命中率,优先改用 URL 区分(如 m. 域名或路径前缀)。

参考:Cloudflare 缓存文档 https://developers.cloudflare.com/cache/;HTTP Cache-Control 规范 https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control

三、一个真实调优案例

某图片站启用 CDN 后命中率只有 68%。查日志发现两个问题:一半流量来自一张带随机时间戳参数的轮播图接口,另一半来自没开 immutable 的雪碧图。处理方式是:轮播图接口走 CDN 的忽略查询参数规则,构建脚本给静态资源文件名加哈希并开 immutable。一周后字节命中率升到 94%,源站带宽费降了 40%。

16IDC 建议

先跑一周基线,记录命中率和回源流量;然后按"TTL → 版本化 → 去动态参数 → Cookie"的顺序逐项优化,每改一项等两三天再看数据,避免一次改太多分不清谁起的作用。对加载速度敏感的场景,命中率从 70% 提到 95%,体感差距非常明显。另外建议把命中率和回源字节数挂进监控面板,跟源站 CPU/带宽联动看,回源突然上涨往往就是命中率波动的先兆。