本文是 CDN 系列的实战篇,使用一个公开内容站点作为贯穿案例:前端构建产物带内容哈希,HTML 入口会随发布更新,登录 API 不进入共享缓存。目标是把接入、更新和排障落实成可验证的操作。内部机制见《CDN 技术体系:从分发原理到工程落地》,选型与全球化取舍见《内容分发体系设计:CDN 架构决策与全球化落地》。
接入前先写清内容策略
CDN 能减少重复获取和远距离传输,但长尾内容、刚发布的对象和动态接口不会拥有同样的命中率。不要先规定“所有静态资源必须超过 95%”,再把任何偏差都归因于配置错误。
| 本例内容 | 缓存策略起点 | 更新方式 |
|---|---|---|
/assets/app.{hash}.js 等不可变资源 |
长新鲜期、immutable | 发布新 URL,保留旧文件 |
/index.html |
no-cache 或满足更新目标的短 TTL | 更新入口,必要时精确失效 |
| 公开版本化图片与下载文件 | 按版本长缓存 | 新对象使用新路径 |
| 允许短暂陈旧的公开查询 | 短 TTL,按业务决定是否 SWR | 验证、过期或失效 |
| 登录 API、私有响应 | 默认 no-store,绕过共享缓存 | 每次请求保持正确身份与权限 |
带 Cookie 不必然意味着响应不可共享,例如纯静态资源可能与 Cookie 无关;没有 Cookie 也不能证明资源公开。初始接入可以采用保守的登录绕过策略,再逐项开放经过验证的例外。
no-cache 允许存储,但重用前需要验证;验证可能返回 304,也可能返回新的正文,并不等于“不回源”。no-store 阻止本次响应被缓存,不能清除此前已存储的所有旧副本。HTTP 缓存规范
选择具体服务与网络,不按品牌猜行为
选型时携带实际的地域流量、文件大小和功能要求,比较同一负载下的体验、回源、合同报价和运维接口。主流供应商可能提供多个不同套餐或网络,不能用一个起步价决定总体成本。
尤其要区分全球服务与中国大陆服务:Cloudflare 的 China Network 和 CloudFront 中国服务有各自的开通与运营条件,“这两个品牌在大陆没有节点”的说法不准确。Cloudflare China Network、CloudFront 中国服务
使用大陆加速服务时按服务商要求核对域名备案等接入条件;全球化的数据处理边界则应另行核对,不能靠节点地图或 GeoDNS 自动满足。架构篇已说明这些区别,接入时要把选择落实到具体账户、网络和合同。
配置域名、回源与 TLS
先在 CDN 添加业务域名并完成所有权验证,获得服务商分配的目标,再修改 DNS。标准 CNAME 不能与根域名必需记录并存;需要根域名加速时,使用 DNS 服务商明确支持的别名或展平能力。
源站地址、HTTP Host 和 TLS SNI 是三个相关但不同的配置:
| 项目 | 本例 | 用途 |
|---|---|---|
| 访问域名 | static.example.com |
用户请求和边缘证书 |
| 回源地址 | origin.example.com |
CDN 连接源站 |
| 回源 Host | 由源站虚拟主机配置确定 | 选择正确站点 |
| 回源 SNI | 与受信任证书名称匹配 | TLS 握手与证书校验 |
源站地址不能解析回同一 CDN 入口而形成循环。用户到 CDN、CDN 到源站分别建立 TLS;后者需要校验证书和名称,不能因为使用内网或自签证书就直接关闭验证,应配置受信任证书链。
提前降低 DNS TTL 可以缩短一部分缓存等待,但不会立即清除已经缓存的旧记录、长连接或应用连接池。切换期间应保留旧链路,观察真实流量收敛。
缓存规则与源站响应头一起验证
下面是 Nginx 的配置片段,假设 /assets/ 只包含不可变且公开的版本化文件,API 后端监听本机 9000 端口。目录、端口与 TLS 需按部署环境补齐:
server {
listen 8080;
server_name origin.example.com;
root /srv/content-site;
location ^~ /assets/ {
try_files $uri =404;
add_header Cache-Control "public, max-age=31536000, immutable";
}
location = /index.html {
try_files $uri =404;
add_header Cache-Control "no-cache";
etag on;
}
location ^~ /api/ {
proxy_pass http://127.0.0.1:9000;
proxy_hide_header Cache-Control;
add_header Cache-Control "no-store" always;
}
}
这里使用固定目录隔离公开静态资源,没有按所有 .js 或 .jpg 后缀一律长缓存。API 用 ^~ 防止后续增加的正则 location 抢走路径。示例统一 API 的 Cache-Control;真实项目若由应用输出细粒度策略,应保留应用的权威配置,避免重复或矛盾响应头。
CDN 控制台规则可能覆盖源站头,最小 TTL、缓存 eligibility、边缘函数也会影响结果。不存在统一的“源站头永远优先”规则,必须检查所用产品。接入后分别验证静态资源、HTML、API、错误响应及带认证请求。
只忽略确实不影响响应的参数
追踪参数可以造成不必要的缓存分裂,但宽度、语言、版本、签名等参数可能改变响应或访问权限。移除参数前检查源站行为,再确认缓存键和回源参数是否分别配置。
不能把 v 从缓存键去掉,却仍依赖 ?v=2 更新版本。签名参数是否进入缓存键也不等于是否执行鉴权:即使复用同一个媒体对象,每次私有访问仍需要正确授权。
跨域按资源用途设置
跨域图片展示、JavaScript 读取响应、模块脚本和字体的要求不同。不能因为静态域名不同就为所有内容盲目反射 Origin。
公开且无凭据的资源可以按需要返回固定的 Access-Control-Allow-Origin: *。按白名单返回具体 Origin 时,要让缓存正确区分相应响应,核对 Vary: Origin 或厂商缓存策略;带凭据的请求还要匹配凭据规则。CORS 不替代资源访问鉴权。
发布:先放新文件,再更新入口
一个可恢复的发布流程包含:
- 构建并记录版本与资源清单,检查所有引用存在
- 上传不可变资源,确认状态码、类型与内容摘要正确
- 更新 HTML 等入口,避免入口引用尚未准备好的文件
- 对需要更新的可变 URL 提交精确 Purge,记录厂商任务 ID
- 从目标地区检查入口版本与关键资源,失败时按已验证方案回退
- 按保留策略清理旧资源,不在发布新入口时立即删除旧文件
版本化文件不需要为内容变更清除旧 URL,但入口更新仍需要新鲜度策略。Purge 通常只影响对应 CDN,不能强制清除浏览器缓存或让已打开页面切换代码。
全站 Purge 会增加冷缓存流量,影响程度取决于原命中、对象热度、请求合并和源站容量。不要用固定“准备五到十倍容量”代替计算;优先缩小失效范围,并按产品能力在失效完成后受控预热。提前预热随后又清空,并不能保住新缓存。
多 CDN 场景下记录每家提交、完成、失败和验证状态,一家成功不能标记整体成功。Purge 服务也需要鉴权、范围校验、重试和审计,不能接收任意外部 URL 或任意回调地址就直接执行。
验证:状态码、内容和缓存状态分别看
以下命令中的域名是示例,需替换成已授权测试的目标。优先使用 GET 观察真实响应;HEAD 可以辅助检查,但其缓存和回源行为不一定完全相同。
curl --fail --silent --show-error --dump-header - --output /dev/null \
https://static.example.com/assets/app.examplehash.js
检查正确对象是否为 200、类型是否正确、Cache-Control 是否符合策略,再结合供应商的缓存状态头和 Age 判断。Cloudflare、CloudFront 等状态字段不同;没有某一个特定头,不等于一定没走 CDN。
切换 DNS 前可以用 --resolve 保持 URL 域名、Host 与 SNI,同时连接选定的测试地址。下面使用文档保留地址,必须换成该服务真实返回且允许测试的地址:
curl --fail --silent --show-error \
--resolve static.example.com:443:192.0.2.10 \
--dump-header - --output /dev/null \
https://static.example.com/index.html
这验证的是当前发起位置到该地址的路径。给不同 IP 贴上“北京、上海”的标签,不会把本机请求变成真正的多地拨测;Anycast 地址还可能路由到不同实际节点。
| 检查 | 需要确认的结果 |
|---|---|
| 新入口 | 正文中的发布版本及引用符合本次发布 |
| 新资源 | 内容摘要匹配,不只是返回 200 |
| 缓存复用 | 同键重复请求的缓存状态符合产品策略 |
| 认证隔离 | 不同账户、匿名与退出后的响应不会串用 |
| Range | 支持时返回正确的 206 与 Content-Range |
| 错误响应 | 404/5xx 不被错误设置成长时间公开缓存 |
| 回退 | 旧入口所需资源仍可获取,业务数据兼容 |
ETag 可以作为版本线索,但不应在所有对象存储或压缩路径中都当作文件 MD5。必要时下载正文验证自己的内容摘要或发布标识。
全球化分流需要真正支持地域策略的 DNS 或流量管理产品。普通 Cloudflare DNS 记录不能通过写上“仅 CN”就变成地域路由;Load Balancing 的 steering policies 是另一项能力,还需核对模式、套餐与目标接入限制。Cloudflare 流量调度策略
日常运营:按异常类型选择检查
| 症状 | 优先检查 | 不能直接得出的结论 |
|---|---|---|
| 命中率下降 | 新内容比例、缓存键、TTL、规则与失效记录 | 一定是 TTL 太短 |
| 用户看到旧内容 | 浏览器、Service Worker、CDN、源站版本 | Purge 提交成功就应全网更新 |
| 某地访问慢 | 解析、建连、HIT/MISS、运营商与终端 | 一定是该地没有节点 |
| 回源失败 | DNS、SNI、证书、Host、网络和源站日志 | 一定是源站应用故障 |
| 源站流量突增 | 失效、冷缓存、新热点、绕过与攻击 | 一定是有人全站 Purge |
缓存合并、回源并发限制和允许范围内的 stale 响应可以缓解冲击,但是否生效必须事先配置并验证。源站返回 503 本身不会让 CDN 自动具备旧响应兜底;如果对象从未缓存、旧值已被硬删除,或者策略不允许陈旧响应,就没有可以复用的内容。
监控至少区分请求命中率、字节命中率、回源请求与字节量、用户延迟、错误率和费用。告警阈值围绕正常基线、源站余量和业务目标设置,不把统一百分比套到所有站点。
成本优化也要核对口径:字节命中率从 90% 提升到 95%,在相同交付量和其他简化条件下可将未命中字节减半,但不会把 CDN 向用户交付的流量减半。图片格式、裁剪和压缩是否有效,应以代表性素材的体积与质量比较为准。
接入完成的标志,是团队知道每类内容如何缓存、怎样更新、怎样判断当前版本,以及异常时先查哪条链路。把这些动作纳入发布记录和持续观测,CDN 才能成为可维护的基础设施。