CDN 加速静态资源:原理、缓存策略与落地配置
把网站做快,最先见效的一步往往不是重构后端,而是把静态资源搬到离用户更近的地方。CDN 加速静态资源听起来只有三个动作——接入、缓存、命中,但真正在项目里落地过的人都清楚,配置面板上每一个看似不起眼的开关,都可能在某个流量高峰变成一封告警邮件。这篇文章不堆概念,只把 CDN 加速静态资源从运行机制讲到缓存设计、从配置清单讲到问题排查,尽量把那些踩过的坑说透。
静态资源为什么值得单独做一层加速
静态资源指的是内容在发布之后不会因为访问者不同而发生变化的文件:HTML 文档、CSS 样式表、JavaScript 脚本、图片与图标、Web 字体、音视频分片、JSON 配置等。它们的共同特征非常明显:体积大、单页请求数量多、内容相对稳定、天然适合缓存。一个内容型页面动辄几十个请求,其中九成以上都是这类文件。
源站的痛点也高度一致。单点机房到全国各地用户的物理距离摆在那里,跨运营商、跨地域的链路抖动和丢包无法靠代码优化解决;每一次新建连接都要付出 DNS 解析、TCP 握手、TLS 协商的开销,而慢速网络下这些开销被成倍放大;再加上出口带宽有上限,一旦遇到营销活动或客户端集中更新,源站带宽会先于 CPU 被打满。把静态资源交给分布在各地的边缘节点承接,等于把「一辆车从仓库出发送到每个人手里」改成「把货提前铺到离用户最近的便利店」。
别把静态和动态混在一起处理
很多站点早期为了省事,所有请求都走同一个域名、同一套规则,结果就是缓存策略只能写得极其保守,命中率长期上不去。合理的做法是先做拆分:静态资源走独立域名,动态接口走另一条链路。这样 CDN 只需要承担可缓存的部分,配置复杂度下降,命中率也更容易做高,排查问题时责任边界也清晰得多。
CDN 加速静态资源的运行机制
要配好 CDN,先得知道一个请求在它内部走了哪些路。整个过程大致可以拆成三段:调度、缓存、传输。
调度:把请求导向最近的节点
最常见的接入方式是在域名服务商处为静态资源域名配置一条 CNAME,指向 CDN 服务商提供的调度域名。用户发起请求时,递归 DNS 会把这个域名解析成某个边缘节点的 IP。选择哪个节点,取决于地理位置、运营商线路、节点负载和健康状态等因素,有的服务商还会结合实时探测数据做动态调整。这一步决定了用户连上的是哪台机器,也决定了后面所有优化的起点。
缓存与回源:命中要快,未命中也不能慢
请求到达边缘节点后,节点会按照缓存键查找本地是否存有该文件的有效副本。命中就直接返回,响应时间基本等于一次网络往返;未命中则回源站取文件,取回后写入缓存再返回给用户。为了进一步降低回源压力,规模较大的 CDN 通常会在边缘节点与源站之间设置中间层节点,让多个边缘节点共享一份回源结果,这就是所谓的回源收敛。
回源这一段其实很关键。很多站点的边缘命中率看起来不错,但回源带宽依旧居高不下,原因往往是回源没有做优化:回源协议还是明文 HTTP、回源域名指向单台机器、没有开启长连接、回源 HOST 配置错导致落到默认站点。把回源链路理顺,通常比单纯延长缓存时间更有效。
传输层与应用层的优化
现代 CDN 在「搬运」这件事上做了大量工作:HTTP/2 的多路复用减少了并发请求的连接开销,HTTP/3 基于 QUIC 把传输握手和加密握手合并,弱网下的表现明显更好;TLS 1.3 缩短了协商轮次;Brotli 与 gzip 压缩能显著缩小文本类资源的体积;图片方面则有自动转码、按需裁剪、格式转换等能力。这些优化单看每一项都不惊人,叠加在同一条链路上就会形成可感知的差异。
缓存策略才是决定效果的那一半
接入 CDN 只是把路修好了,真正决定这条路跑得快不快的,是缓存策略。同一个 CDN 服务商,两个配置水平不同的站点,命中率可能相差一倍以上。关于缓存头的语义,MDN 的 Cache-Control 文档解释得很清楚,这里只讲怎么用在静态资源上。
强缓存:让浏览器和边缘节点都少发请求
对于带内容指纹的文件,可以直接设置较长的 max-age,并配合 immutable 告诉浏览器在有效期内不必再验证。需要注意的是 public 与 private 的区别:public 允许 CDN 等共享缓存保存副本,private 只允许浏览器缓存,静态资源通常应该用 public。s-maxage 则是专门针对共享缓存的存活时间,可以在浏览器缓存和边缘缓存之间做出区分。
协商缓存:作为兜底而不是主力
ETag 和 Last-Modified 的作用是「问一下有没有变」。它比完全重新下载划算,但仍然要付出一次往返的成本,因此不适合作为静态资源的主要策略。更常见的组合是:文件名带哈希的 JS、CSS、字体用长强缓存;不带哈希的 HTML 入口文件用较短的缓存时间,并依赖协商缓存保证内容及时更新。
文件指纹与发布流程要配套
「长缓存 + 文件名哈希」是静态资源加速最经典的搭配。内容一变,文件名跟着变,URL 就是新的,缓存自然失效,不需要依赖手动刷新。反之,如果文件名固定而缓存时间又很长,每次发布都要全网刷新,一旦某个节点刷新失败,用户就会看到新旧资源混杂的页面。构建工具生成的 contenthash 就是为此存在的,务必确认它对所有产物都生效,包括字体、图标和异步加载的分包。
缓存键设计:别让参数毁了命中率
默认情况下,完整的 URL 连同查询参数都会被纳入缓存键。带随机数的时间戳参数、用于统计的 utm 参数、每次请求都不同的签名参数,都会让同一个文件在缓存里被存成无数份。常见的处理方式是在控制台配置忽略指定参数或忽略全部参数,只按路径缓存;对于确实需要区分内容的参数,则保留并纳入键值。这一步做不做,命中率往往不在一个量级上。
落地配置清单
把上面的原理翻译成配置项,通常需要逐条确认以下几件事。
域名、证书与回源
静态资源建议使用独立域名,既方便单独设置缓存,也能避免与主站共享 Cookie 带来的额外请求头开销。证书要覆盖该域名并开启自动续期;回源协议优先选 HTTPS,并正确设置回源 HOST 与 SNI,避免请求落到源站的默认站点;源站侧最好保留多个回源地址做负载与容灾。
安全与跨域
字体文件和通过脚本发起的请求会受同源策略限制,需要 CDN 返回正确的 Access-Control-Allow-Origin。防盗链方面,可以通过 Referer 白名单或带时间戳的鉴权 URL 限制资源被第三方站点盗用,注意不要把自家 App 或小程序的空 Referer 请求误伤。若使用了鉴权,记得让缓存键包含必要的鉴权字段或设置合理的过期策略。
压缩、图片处理与协议
文本类资源开启 Brotli 或 gzip,注意只对合适的内容类型压缩,避免对已压缩的图片、视频重复压缩浪费 CPU。图片可以开启自动格式转换与尺寸适配,按终端能力返回更小的文件。协议层面开启 HTTP/2 或 HTTP/3,并确认源站与 CDN 之间的协议版本不会成为瓶颈。
预热、刷新与灰度
大版本发布前对核心资源做预热,可以避免发布瞬间大量请求同时回源造成的抖动;对于无法改名的入口文件,才使用刷新功能,并把刷新范围控制到最小。有条件的话,先让小流量节点或灰度环境验证配置,再全量放开。
常见问题与排查思路
问题一:明明改了文件,用户看到的还是旧内容。先确认边缘节点是否命中旧缓存,再检查是否只刷新了目录而没有刷新具体 URL,最后回头看发布流程是不是漏了文件名哈希。问题二:命中率长期偏低。逐项排查缓存时间是否过短、缓存键是否被随机参数撑大、响应是否带了 no-cache 或 Set-Cookie、状态码是否被排除在缓存之外。问题三:回源带宽异常高。常见原因是缓存时间太短、文件体积过大、回源未开长连接,或者多个边缘节点各回源一次而没有走中间层收敛。问题四:跨域请求被浏览器拦截。检查响应头是否在命中缓存时依然完整返回,有些配置只在回源响应里加头,命中后就丢了。问题五:不同地区体验差异明显。可能是节点覆盖不足,也可能是某个运营商线路的回源链路质量不佳,需要结合分地域的监控数据判断。
怎么衡量一次加速是否成功
只看「网站变快了」这种主观感受是不够的。至少需要盯住几组数字:边缘缓存命中率与回源率、首字节时间、资源下载速度、5xx 错误率,以及真实用户侧的加载指标,尤其是最大内容绘制这类与静态资源强相关的指标。数据要分地域、分运营商、分资源类型来看,整体平均值往往会掩盖局部问题。
在横向对比方案时,可以把 Cloudflare、Akamai、阿里云 CDN、腾讯云 CDN 这类服务商的节点分布、缓存规则灵活度和日志能力放在同一张表里评估,而不是只比价格。关于性能指标的定义与优化方法,web.dev 上的资料可以作为参考。
最后是一个容易被忽略的建议:把 CDN 配置当成代码来管理。缓存规则、回源设置、鉴权策略都应该有记录、有评审、可回滚。静态资源加速不是一次性工程,它随发布流程、业务形态和流量结构持续变化,配置的可维护性最终会体现在每一次上线的稳定性上。





