前言
在上一篇我讲了《CDN的缓存机制是怎样的》后,不少站长在给网站接入 CDN 之后,都会遇到一个非常现实的问题:访问速度看起来提升有限,源站流量却依旧居高不下,甚至在高峰期仍然频繁触发带宽告警。进一步查看 CDN 面板,往往会发现一个核心指标异常——缓存命中率长期偏低。理论上 CDN 的价值在于“命中即分发、未命中才回源”,但现实情况却恰恰相反。结合大量实际部署经验来看,这种问题并不源于 CDN 节点性能,而是缓存策略没有真正贴合业务结构,导致 CDN 形同虚设。
一、先搞清楚一件事:不是所有网站都应该追求极高命中率
在优化 CDN 命中率之前,必须先校准预期,否则很容易陷入“为了命中率而命中率”的误区。不同类型的网站,其合理的缓存命中区间本身就存在明显差异。以静态内容为主的博客、企业官网、下载站,本身页面结构稳定、更新频率低,CDN 命中率通常可以长期维持在较高水平;而包含登录态、个性化内容的站点,即便做了合理缓存,命中率也不可能无限接近 100%。如果脱离业务类型去看命中率数字,本身就是一种错误判断。
下面是一个更贴近真实业务的参考区间:
如果你的站点属于前两类,但命中率长期低于 50%,那基本可以确定问题并不在访问量,而在缓存策略本身。
二、命中率低的根因,往往集中在缓存头、参数和 Cookie 上
从实际排查案例来看,命中率异常低的 CDN 项目,问题通常不是“很多”,而是反复集中在几个地方。最常见的情况是源站返回的缓存控制头不合理,很多框架或程序默认会携带 Cache-Control: no-cache 或极短的 max-age,CDN 在这种情况下只能频繁回源,即使资源内容本身几天都不会变化。其次是 URL 参数设计混乱,例如时间戳、统计参数、广告追踪参数混在静态资源请求中,CDN 会将其视为不同资源,最终导致缓存被无限打散。第三个经常被忽略的问题是 Cookie 影响,没有对静态与动态资源进行拆分,导致图片、JS、CSS 请求中也携带 Cookie,从而被 CDN 判定为不可缓存请求。
一个非常典型的错误示例如下:
GET /static/app.js?utm=google&time=1699999999
Cookie: session_id=xxxx
Cache-Control: no-cache
这种请求即便访问一万次,CDN 也只会认为是“一万次不同请求”,命中率自然不可能提升。
三、真正有效的做法:按内容类型设计缓存策略,而不是“一刀切”
要想让 CDN 真正发挥作用,缓存策略必须围绕业务内容来设计,而不是简单套用默认规则。通常更合理的方式是将资源分层处理:静态资源采用长缓存策略,通过文件名 hash 或版本号控制更新;半动态内容设置中等缓存时间,既减少回源又保证内容可控;强动态内容则明确绕过缓存,避免错误命中。这样做的核心目标不是让所有请求都命中 CDN,而是让“应该命中的请求”稳定命中。
在实际配置中,可以采用类似下面的思路:
示例(Nginx):
location ~* \.(js|css|png|jpg|jpeg|gif|svg)$ {
expires 30d;
add_header Cache-Control "public, max-age=2592000";
}
在自建 CDN 场景下,这类策略往往更容易落地。相比公有 CDN 的固定规则,自建 CDN 可以对缓存 Key、参数过滤、Cookie 透传进行更细粒度控制,这也是越来越多中大型站点选择 自建 CDN 解决方案 的原因之一。以 99CDN 为例,其核心优势并不在“节点数量”,而在于缓存规则高度可控,适合已经有稳定业务模型、对命中率和回源成本敏感的网站。
总结
CDN 命中率低,并不是一个靠“堆带宽”就能解决的问题,它本质上是缓存策略与业务结构不匹配的结果。只要从源站缓存头、URL 参数设计和 Cookie 使用这三个关键点入手,再结合内容分层缓存的思路,大多数站点的命中率都会出现明显改善。对于访问量已经成规模的网站来说,与其不断升级源站配置,不如让 CDN 真正承担起该承担的流量压力,而这正是自建 CDN 架构最核心的价值所在。
评论区