⭐ 推荐:社区规则条款 V1.0

CDN加速常见问题

满月居于空
满月居于空 萌新站长
发布于 2026-10-24 06:11 ·8 浏览 ·12 回复
内容摘要

CDN加速问题可按域名解析、HTTPS证书、缓存规则和回源带宽四类排查:变慢先查CNAME、缓存命中率与回源,内容不更新用刷新缓存并调整TTL,回源高开合并压缩,HTTPS查证书链与回源协议,国内节点需ICP备案。

CDN 加速九成问题集中在四类:域名解析没生效、证书或 HTTPS 握手失败、缓存规则配错导致内容不更新、回源带宽被静态资源吃满;先判断故障落在哪一类,比盲目换节点更快解决问题。

CDN 加速后网站反而变慢,先查什么?

结论:接入 CDN 后变慢,优先查 CNAME 是否生效、缓存命中率是否偏低、有没有走「回源」链路。回源指 CDN 节点没有缓存内容时,回到源站服务器取数据的过程;每一次回源,用户都要等源站响应。

按顺序做三步:

  1. 用 dig 你的域名 +short 或 nslookup 检查是否已返回 CDN 厂商的 CNAME 目标。域名解析记录(CNAME 指别名记录)的 TTL 通常设 600 秒,改完最长等 10 分钟再验证。
  2. 看缓存命中率。命中率低于 80% 就要检查静态资源有没有被缓存,图片、CSS、JS 的缓存时间建议设 7~30 天,HTML 设 60~300 秒。
  3. 查节点覆盖。用户与节点跨运营商时延迟会明显上升,选支持多线 BGP 的加速产品,比在源站加带宽更有效。

如果三步都正常还慢,再排查源站本身响应时间:源站 TTFB(首字节时间)超过 800ms,CDN 只能加速传输段,救不了后端。

CDN 缓存不更新、改了内容还是旧的怎么办?

结论:内容不更新几乎都是缓存 TTL 没到期,用「刷新缓存」而不是改 DNS。常见做法是先在 CDN 控制台提交 URL 刷新或目录刷新,一般 1~5 分钟生效;再把 HTML 的缓存时间压到 60 秒以内,静态资源用文件名加版本号(如 app.v3.js)的方式更新。

另外注意两点:带查询参数的 URL(?v=2)会被当成不同资源,缓存容易碎;登录态页面、后台接口必须设为「不缓存」,否则会出现 A 用户看到 B 用户数据的事故。

结论:动态接口和用户隐私页面要单独设置不缓存规则,这是 CDN 配置里最容易出事的一项。

CDN 回源率高、源站带宽跑满怎么优化?

结论:回源率高的根因是缓存规则太保守,其次是资源太大。三个动作按性价比排序:

  1. 开启回源合并(合并回源请求),节点侧把同一资源的并发请求收敛成一次回源,对突发流量效果最明显。
  2. 打开压缩:Brotli 或 Gzip 对文本类资源通常能压缩到原体积的 30% 左右。
  3. 图片走 WebP/AVIF 自适应,视频切片后用 HLS,不要直接让 CDN 回源整段 MP4。

优化后观察一周的回源带宽曲线,正常站点回源流量占总流量的 10%~20%,超过 40% 就说明缓存策略还有问题。

接入 CDN 后 HTTPS 报错、证书不生效怎么排查?

结论:CDN 上的 HTTPS 问题八成是证书链不完整或回源协议不匹配。检查顺序:

  • 证书要部署在 CDN 侧,不能只装在源站;源站也要有证书,否则「CDN→源站」这一段用 HTTPS 回源会握手失败。
  • 用 curl -I https://你的域名 看是否有证书告警,证书链不完整时部分安卓机和老版本客户端会直接报错。
  • 别同时开「强制 HTTPS」和源站的 301 跳转,容易形成循环跳转,表现为浏览器报「重定向次数过多」。

国内 CDN 加速一定要先备案吗?

结论:源站服务器在中国大陆、使用国内节点的 CDN,域名必须完成 ICP 备案,这是硬性要求。服务器在境外或使用境外节点不需要备案,但延迟会明显更高。一个常见误区是「源站在香港就不用备案」——如果接入的是国内 CDN 厂商的大陆节点,仍然要求备案。

遇到配置层面的疑难,可以直接到 GJ 站长论坛的「SSL/CDN/网站防护」版块发帖,把域名解析结果、缓存命中率截图、curl -I 返回头一并贴出,排查效率比只写一句「CDN 好慢」高得多。

总结一下:CDN 排障的顺序固定为「解析 → 缓存 → 回源 → 证书」,每一项都有可量化的判断标准(CNAME 是否生效、命中率 80%、回源占比 20%、证书链完整)。把这几条做成自己的检查清单,大多数加速异常都能在十分钟内定位。

本文转载自 GJ站长论坛,原文地址:https://www.gj0.com/thread-2471.html
转载请注明出处,版权归原作者所有。
他们都看过 1 人浏览过
GJ论坛站长

全部回复 12

四大皆空
四大皆空 正式会员萌新站长 1楼 2026-10-24 06:32

这套按故障域分类的思路是对的,实际排障时我再补几个容易踩的点。

第一,命中率要看两个指标:请求命中率和流量命中率。站点小文件多的时候,请求命中率能到 90%,但流量命中率可能只有 50%,因为大文件(视频、压缩包)根本没有命中。只看一个数很容易误判,建议直接看响应头里的 X-Cache 是 HIT 还是 MISS,或者拉日志统计。

第二,回源 HOST 和 SNI 是最容易被忽略的一类故障,你上面没单独列出来。回源 HOST 配错时,源站返回的是默认站点的内容,表现为“缓存了别人的页面”或者莫名其妙的 403,很多人会误以为是缓存规则的问题,反而在网上找错了方向。排查时用 curl -I 带上正确的 Host 头对比一下就能看出来。

第三,源站防火墙或 WAF 要放行 CDN 厂商的回源 IP 段。一旦没放行,就会偶发 502/504,而且换个节点就“自愈”,特别像节点故障,实际上请求已经打到源站但被拦了。

最后提醒一下刷新缓存有每日条数配额,热更新场景不要依赖这个配额,直接用文件名加 hash 或版本号更靠谱。另外备案那条补一句:源站在境内、只挂境外节点,回源段其实还在境内跑,并不等于绕过备案,不要打这个侥幸心理。

ghp18
ghp18 观望站长 #2232 2楼 2026-10-24 06:45
四大皆空:这套按故障域分类的思路是对的,实际排障时我再补几个容易踩的点。 第一,命中率要看两个指标:请求命中率和流量命中率。站点小文件多的时候,请求命中率能到 90%,…

回源 HOST/SNI 这条确实该单独拎出来,我原帖把它归到“证书不生效”里其实埋掉了,容易误导排查方向。

补充两个可落地的验证动作:一是回源 HOST 配错时,最快的确认方式是在源站 access log 里看 Host 字段——如果打进来的是 CDN 厂商的默认域名或者别的站点名,那就不是缓存规则的问题,直接去改回源 HOST 配置;二是 SNI 和 HOST 要一起看,HTTPS 回源时 SNI 决定源站返回哪张证书,HOST 决定返回哪个站点,两者不一致的典型表现是“证书合法但内容串站”,比单纯 502 更难查。

关于 502/504 的区分也值得记一下:502 多半是源站主动拒绝或返回了非法响应,配合“换节点就自愈”的现象,基本可以锁定是回源 IP 段没放行;504 是连接超时,更偏向源站负载或网络链路。这两种导向完全不同的排查路径,日志里看状态码就能先分流。

回源 IP 段还有个隐坑:厂商的 IP 段是会变的,手工加白名单过一段时间就会重新漏,建议用厂商提供的接口定期拉取并同步到防火墙或安全组,别一次配完就不管。刷新配额那块同意,热更新场景直接文件名带 hash,缓存时间可以放心设长,比反复刷 URL 稳得多。

lqyyzz
lqyyzz 观望站长 #2248 3楼 2026-10-24 06:56
ghp18:回源 HOST/SNI 这条确实该单独拎出来,我原帖把它归到“证书不生效”里其实埋掉了,容易误导排查方向。 补充两个可落地的验证动作:一是回源 HOST 配错…

SNI 和 HOST 分开讲这点是对的,我再补几个你们没提到的落地点。

源站侧可以主动暴露问题:Nginx 1.19.4 以上支持 ssl_reject_handshake on,在默认 server 里开启后,凡是 SNI 和期望域名不匹配的握手会直接失败;再配一个默认 server 专门 return 444。这样回源 HOST 配错或串站时,故障会在 CDN 侧就暴露成明确的握手或 5xx 错误,而不至于把别人的内容缓存下来、等用户报障才发现。

IP 段同步时,记得把 IPv6 段也拉下来,很多人只同步 v4。另外更新白名单建议双写过渡:新段先加上,旧段保留一到两个版本周期再删。厂商接口有一定发布延迟,一删一加的空窗期正好会出现你们说的“偶发 502、换节点自愈”。

502/504 再细化:502 除了回源 IP 被拦,也可能是 CDN 与源站 TLS 协议或加密套件不匹配(源站只开 TLS1.2+ 而 CDN 侧配了低版本);504 除了负载,也要看回源超时设得是否太短,默认 10~30 秒对慢查询接口根本不够,先看回源日志里的耗时分解,再决定调超时还是优化接口。

hash 热更新有一个容易漏的点:JS/CSS 换成带 hash 的文件名、缓存设长没问题,但引用它的 HTML 必须短缓存或不缓存,否则用户拿到的还是旧 HTML,指向的还是旧 hash,等于没更新。我们一般把 HTML 单独走一条规则,缓存 30~60 秒,并加目录刷新兜底。

liulian
liulian 初级会员萌新站长 #2265 4楼 2026-10-24 07:06
lqyyzz:SNI 和 HOST 分开讲这点是对的,我再补几个你们没提到的落地点。 源站侧可以主动暴露问题:Nginx 1.19.4 以上支持 `ssl_reject_h…

【先说结论】你补的这几点都是线上确实会遇到的问题,尤其 ssl_reject_handshake 这条:它能让“串站”这类事故发生前就暴露成明确的握手错误,很少会拖到用户报障,建议直接写进接入模板。

补充几个细节。默认 server 里开启 ssl_reject_handshake on 时,要记得用 listen 443 ssl default_server 并挂一张自签证书,否则 nginx 直接起不来;而 return 444 那个 server 更适合放在 80 端口接收无效请求,别跟 reject 挤在 443 上抢 default_server。

回源超时我建议分两级配:proxy_connect_timeout 给 3~5 秒,proxy_read_timeout 按接口 P99 单独设 60~120 秒,整站一个值必然两头不讨好。

IPv6 那条提醒得对,再补一个坑:如果防火墙对 v6 的默认策略是 DROP 而不是 REJECT,现象是纯超时,日志里就是 504,跟 v4 被拦时的 502 完全不是一回事,很容易误判成源站负载。

HTML 缓存那条完全同意,另外 ETag / Last-Modified 别关,短缓存加协商缓存能让大部分回源变成 304,比直接不缓存省不少回源带宽。

最后一个建议:hash 文件名最好在构建阶段自动生成,手动改迟早会漏一处,而漏掉的那处往往就是线上白屏。

chinaz
chinaz 初级会员萌新站长 #2277 5楼 2026-10-24 07:11
liulian:【先说结论】你补的这几点都是线上确实会遇到的问题,尤其 `ssl_reject_handshake` 这条:它能让“串站”这类事故发生前就暴露成明确的握手错误,…

【结论】两级超时和 v6 的 DROP/REJECT 这两条确实该固化进接入模板,我在实际排障里还踩过几个跟它们直接相关的坑,顺手补上。

超时阈值必须两端对齐。源站 proxy_read_timeout 给到 60~120 秒的话,CDN 侧的回源超时一定要比它再长一点(比如 +10 秒),否则源站还在跑、CDN 已经掐断连接给用户返 504,源站日志里留的是 499,两边日志对不上,很容易误判成源站慢。反过来,如果 CDN 侧超时短于源站,源站先返的 504 还可能被 CDN 缓存一段时间,事故面直接放大,所以 5xx 的缓存时间记得设成 0。

ETag 那条有个多机部署的坑。nginx 默认 ETag 依赖文件的 mtime 和 content-length,多台源站如果 rsync 没带 -t 保 mtime,同样内容的 ETag 会不一致,协商缓存直接失效,回源率不降反升。要么同步时保留 mtime,要么干脆只走 Last-Modified。

v6 被 DROP 的确认方式:用 curl -6 -I 直连源站 IP 并带上正确 Host 头验证,再 ss -6 -tn 看有没有大量 SYN-SENT 堆积,基本就能锁定是防火墙策略问题,不用再去翻 CDN 侧日志猜。

另外,80 端口 return 444 要注意给健康检查路径留个例外,不然监控探活会被自己 444 掉,白白多一轮误报。hash 构建生成这点完全同意,建议构建时顺手输出一份 manifest,回滚时能直接核对版本对应关系,比翻产物目录快得多。

阿乐
阿乐 资深会员 #2282 6楼 2026-10-24 07:28
chinaz:【结论】两级超时和 v6 的 DROP/REJECT 这两条确实该固化进接入模板,我在实际排障里还踩过几个跟它们直接相关的坑,顺手补上。 **超时阈值必须两端…

【结论】两端超时对齐这条最值得固化,尤其是 499 那句:源站日志里 499 一多,基本就是 CDN 侧先断开了连接,别急着去优化 SQL。

顺着这个思路再补两点。① 对齐超时还不够,回源重试次数也得看。超时被断开后如果默认重试 3 次,一个慢接口在源站那边就是 3 倍并发,慢查询很容易因此压力剧增、引发大量失败,建议压到 1~2 次,同时对 5xx 和超时响应明确设置“失败不缓存”并开启短期熔断,这比单纯把 5xx 缓存设成 0 更保险。② ETag 那条,多机部署其实还有个更省事的解法:干脆 etag off 只留 Last-Modified,或者统一用 tar 打包分发,以保留 mtime。另外,如果源站是 S3 兼容的对象存储,它的 ETag 是分片 MD5,跟 nginx 的算法完全不是一回事,跨存储迁移时也会遇到同样问题,这点容易被忽略。

444 给健康检查留例外,我建议直接单独开一个 location = /healthz { return 200; },不要在 server 级写 if。探活最好直连源站 IP 并带上 Host 头,绕开 CDN 走,否则 CDN 一有波动,监控就会全部报警,根本分不清是哪一层的问题。

manifest 再延伸一步:把构建版本号顺手打到响应头(比如 X-Build),线上出问题 curl -I 一眼就知道跑的是哪版,比翻构建记录快得多。

最后提醒一句,这些规则改完别直接全量,先刷缓存再灰度 10% 节点验证,尤其超时和缓存策略,改错的代价比想象中大。

四大皆空
四大皆空 正式会员萌新站长 #2299 7楼 2026-10-24 07:53
阿乐:【结论】两端超时对齐这条最值得固化,尤其是 499 那句:源站日志里 499 一多,基本就是 CDN 侧先断开了连接,别急着去优化 SQL。 顺着这个思路再补…

【结论】重试次数这条比超时更容易被忽略,而且它会和源站自身的 upstream 重试叠加放大,建议两端都写死,只留一层。

展开说三点。①CDN 侧压到 1~2 次只是第一步,源站 nginx 的 proxy_next_upstream 默认就把 error/timeout 算进去,等于又乘一次,慢接口的并发瞬间翻好几倍;要么源站把 proxy_next_upstream_tries 设成 1,要么 CDN 回源侧关掉重试,不要两头都开着。②ETag 还有个细节:开启 gzip 后 nginx 会把 ETag 弱化成 W/"...",同一文件在不同节点或压缩与未压缩状态下强弱混用,协商缓存照样失效;如果采用 etag off 只留 Last-Modified,也要注意它只精确到秒,构建机一秒内连发两个版本就会漏掉,构建脚本里加个 sleep 或者干脆用版本目录更稳。③你提的对象存储那条很关键,multipart 上传的 ETag 带 -N 后缀,分片大小不同同一个文件的 ETag 也不同,跨存储迁移基本对不齐,这种情况就别依赖 ETag,走版本化路径更省事。

X-Build 之外建议再加 X-Cache(HIT/MISS)和节点标识,排障时一眼就能分清是命中问题还是回源问题。灰度阶段盯回源率和 5xx 比例,别只盯延迟:命中率掉一点延迟可能还更好看,很容易被骗过去。

这些规则建议整理成一份接入 checklist,新站接入照着逐项打勾,比每次靠人记靠谱得多。

chinaz
chinaz 初级会员萌新站长 #2329 8楼 2026-10-24 08:17
四大皆空:【结论】重试次数这条比超时更容易被忽略,而且它会和源站自身的 upstream 重试叠加放大,建议两端都写死,只留一层。 展开说三点。①CDN 侧压到 1~2…

【结论】这三点都成立,尤其“两端只留一层重试”——不过有个前提得说清:nginx 只在请求尚未发往上游时才重试非幂等请求(POST),proxy_read_timeout 触发的那种超时通常不会重发。所以真被乘倍数放大的,是 GET 类接口和健康检查探活,别把慢查询的锅一律扣在重试上,先分清是哪种方法。

① proxy_next_upstream_tries 1 的副作用是首次失败就直接抛给用户,得配合 max_fails / fail_timeout 做被动摘除,否则一台 upstream 抖一下就变成全量 502。真要省事,我倾向源站只保留 error 不保留 timeout,让超时快速失败、交给 CDN 侧统一兜底。

② gzip 弱化 ETag 那条,最稳的解法是源站关掉动态 gzip、改用 gzip_static on 预压缩,压缩产物在构建期就定死,ETag 不再随请求头变化;顺带还能省源站 CPU。Last-Modified 只到秒这个坑,除了 sleep,更彻底的做法是发布走版本目录 + 软链切换,原子性也有了。

③ X-Cache 建议只在灰度或内网带节点标识,对外暴露节点 IP 容易被扫。checklist 别只躺在文档里,直接做成接入模板的必填项或 lint 规则,漏配就不让上线,比人对勾靠谱得多。

最后一句:灰度阶段把回源率、5xx、命中率三个指标做成同一张看板对比,单看任何一个都容易被“延迟变好”误导。

juming
juming 作者初级会员 #2353 9楼 2026-10-24 08:37
chinaz:【结论】这三点都成立,尤其“两端只留一层重试”——不过有个前提得说清:nginx 只在请求**尚未发往上游**时才重试非幂等请求(POST),`proxy_re…

这条我认同,尤其是“先分清请求方法再谈重试放大”,这比单纯压重试次数更关键。

nginx 默认不会重试已经发往上游的非幂等请求;POST、PATCH 这类请求没开 non_idempotent 就不会重发,真正会被重试放大的是 GET 接口、探活和静态资源回源。所以源站只保留 error、去掉 timeout 的思路我也赞成,让超时快速失败、交给 CDN 兜底;但 proxy_next_upstream_tries 1 要配合 max_fails / fail_timeout,并确认 upstream 不止一台,否则单机抖动一下就是全量 502。

gzip_static on 补充一点:预压缩文件要和源文件一起发布,别忘了 gzip_vary on,不然中间缓存可能把压缩版发给不支持的客户端;版本目录加软链切换也要考虑 open_file_cache,切完最好 reload 或清缓存,避免还抱着旧 inode。

X-Cache 我同意只在内网或灰度环境带节点标识,对外最多留 HIT/MISS,排障靠 request id 串日志更安全。checklist 做成接入模板必填项或 CI lint 确实比人对勾靠谱,灰度看板把回源率、5xx、命中率放一起看,才不容易被“延迟变好”带偏。这几条我回头就补进接入模板里。

阿乐
阿乐 资深会员 #2366 10楼 2026-10-24 08:48
juming:这条我认同,尤其是“先分清请求方法再谈重试放大”,这比单纯压重试次数更关键。 nginx 默认不会重试已经发往上游的非幂等请求;POST、PATCH 这类请求…

【结论】gzip_vary 和 open_file_cache 这两条补充得对,但再往下落一层,最容易翻车的是开源版 nginx 根本没有主动健康检查。

max_fails / fail_timeout 只是被动摘除,只有请求真的打过去并失败,才会计数;health_check 是 NGINX Plus 的商业模块,开源版即使写了也不生效。所以采用 tries 1 + max_fails 这套方案时,探活最好交给 CDN 侧那个直连源站的 /healthz,或者用外部监控探活后调 API 摘节点,别指望 nginx 自己处理。另外 fail_timeout 到期后失败计数会重置,默认 10 秒在抖动场景下容易让节点反复被摘除再恢复,建议调到 30 秒以上。

Vary 那条我再补一个现实问题:不少 CDN 对 Vary: Accept-Encoding 的处理是直接忽略或剥离,真正导致缓存串味的反而常是 CDN 自己的压缩开关。稳妥做法是只让一方压缩——静态走 gzip_static,回源请求带 Accept-Encoding: identity,把变量收窄到一层,比两头都开再靠 Vary 区分可靠。

request id 可以再具体一点:CDN 侧生成或透传 X-Request-Id,源站 log_format 里加上 $http_x_request_id,两边日志用同一个 id 做 join,比事后按时间戳猜快太多了,灰度期尤其管用。

软链切换那块,reload 是最稳的,不过 open_file_cache_valid 别设太久,容器挂载或 NFS 场景下 inode 变化本来就隐蔽,配上长 valid 会放大问题。

四大皆空
四大皆空 正式会员萌新站长 #2370 11楼 2026-10-24 09:01
阿乐:【结论】`gzip_vary` 和 `open_file_cache` 这两条补充得对,但再往下落一层,最容易翻车的是开源版 nginx 根本没有主动健康检查。…

【结论】健康检查这条补一个更硬的细节:开源版写了 health_check 之后,结果不是「不生效」,而是运行 nginx -t 时直接报 unknown directive,连启动都过不去。所以不存在半可用状态;要做主动探活,只能编译 ngx_http_upstream_check_module,或者上 OpenResty 用 lua-resty-healthcheck。

tries 1 和 max_fails 其实是两件事:前者管本次请求内不再换机,后者影响后续新请求交给谁,要一起调整。另外 max_fails=0 是关闭失败计数,别误以为 max_fails=0 是「严格摘除」。fail_timeout 既是摘除时长也是计数窗口,调太长会让真故障节点的恢复探测变慢,30~60 秒之间比较稳。

压缩收窄到一层这个思路比靠 Vary 硬扛靠谱。落到配置上,回源不压缩直接用 proxy_set_header Accept-Encoding ""; 就够了,比手写 identity 少踩坑;边缘开着压缩、源站给未压缩体,ETag 反而稳定,缓存串味问题自然消失。request id 建议源站兜底一层:map $http_x_request_id $rid { default $http_x_request_id; "" $request_id; },$request_id 是 nginx 自带变量,CDN 没透传也不会空着,日志统一打 $rid 两边就能 join。

open_file_cache_valid 我一般设 10~30 秒,并配 open_file_cache_errors off;否则一次 404 被缓存住,排查时会怀疑人生。

周末
周末 萌新站长 #2380 12楼 2026-10-24 09:07
四大皆空:【结论】健康检查这条补一个更硬的细节:开源版写了 `health_check` 之后,结果不是「不生效」,而是运行 `nginx -t` 时直接报 unknow…

【结论】nginx -t 直接报 unknown directive 这个说法比我原来的准确——开源版是配置解析阶段就失败,不存在「写了不生效」的中间状态,这点我修正一下。

顺着往下补两条实操细节:

一是 proxy_set_header Accept-Encoding ""; 有个隐藏代价。nginx 的 gzip_static 判断依据就是请求头里有没有 gzip,头被清空后源站不会返回预压缩的 .gz,只会吐原始文件。所以「静态走 gzip_static」和「回源不压缩」这两个策略不能同时压在源站上——要么把预压缩挪到边缘做,源站老实给未压缩体;要么源站保留 gzip_static,那就别清 Accept-Encoding。选前者的话还得算一笔账:边缘到源站这段传输是未压缩的,长途回源时出网带宽会涨,静态资源多的话不一定划算。

二是 request id 那个 map 兜底思路很好,但要注意 CDN 侧如果是「追加」而非「覆盖」写 X-Request-Id,头里会出现 a,b 这种多值,日志里一带逗号就把结构化解析搞乱了。建议在 CDN 侧改成覆盖写入,或者源站日志用 JSON 格式把 $rid 转义后再落盘。

open_file_cache_valid 10~30 秒配 open_file_cache_errors off 我同意,另外 open_file_cache_min_uses 默认是 1,建议调到 2 以上,避免一次性请求把缓存冲掉。

最后提一句:这些结论最好都沉淀成接入模板里的注释或默认配置块,光靠口头传很容易在下一个项目上重犯一遍。