
源站上的文件已经存在,直接访问源站也能返回 200,但用户通过 CDN 域名访问时仍然看到 404。这类故障经常发生在静态资源刚发布、对象存储权限刚调整,或站点从旧路径迁移到新路径之后。
404 不一定来自当前源站。CDN 节点可能缓存了此前的错误响应,也可能一直在请求另一条缓存键、另一台源站或另一条回源路径。排查时要把“浏览器看到的 404”和“源站现在是否有文件”分开验证。
先确认 404 是谁返回的
先保留完整响应头,不要只看浏览器错误页:
curl -sS -D - -o /dev/null https://static.example.com/assets/app.js curl -sS -D - -o /dev/null https://origin.example.com/assets/app.js
重点比较状态码、Date、Age、Via、X-Cache、Server、CF-Cache-Status 等字段。不同 CDN 使用的响应头并不相同,但判断思路一致:如果 CDN 域名返回 404,源站域名返回 200,并且 CDN 响应带有命中或缓存年龄信息,错误响应被缓存的可能性很高。
若源站只能通过指定 Host 访问,测试时要模拟 CDN 的回源请求:
curl -sS -D - -o /dev/null \\ -H 'Host: www.example.com' \\ http://203.0.113.10/assets/app.js
直接访问 IP 返回 200,带 Host 后返回 404,通常说明虚拟主机、站点目录或回源 Host 配置不一致。此时刷新 CDN 缓存只能短暂掩盖问题。
404 为什么会被 CDN 缓存
CDN 缓存错误响应可以减少重复回源,防止不存在的路径持续打到源站。某些平台对 4xx、5xx 有默认错误缓存时间,也允许管理员单独设置。源站的 Cache-Control、CDN 缓存规则和产品默认值都可能影响保留时间。
典型过程很简单:
用户在文件发布前请求
/assets/app.js,源站返回 404。CDN 节点缓存这次 404。
文件随后上传成功,但节点仍在错误缓存有效期内继续返回旧响应。
如果 URL 曾经指向不存在的文件,或者发布流程先更新 HTML、后上传静态资源,这个时间窗口更容易出现。
排查缓存键是否一致
刷新了一个 URL,访问仍然报错,常见原因是实际请求落在另一条缓存键上。逐项检查:
HTTP 与 HTTPS 是否被分开处理;
www.example.com与static.example.com是否属于不同加速域名;查询参数是否参与缓存键,例如
app.js?v=20260924;URL 大小写是否不同,例如
/Assets/app.js与/assets/app.js;路径是否经过重写、目录补全或尾部斜杠转换;
Accept-Encoding、设备类型、Cookie 或自定义请求头是否进入缓存键。
可以对几个疑似变体分别取响应头:
curl -I 'https://static.example.com/assets/app.js' curl -I 'https://static.example.com/assets/app.js?v=20260924' curl -I 'http://static.example.com/assets/app.js'
只有带新查询参数的地址返回 200,往往说明旧缓存键仍保存着 404。这个测试适合定位,不能长期依赖随机参数绕过缓存,否则同一文件会产生大量副本。
确认文件确实发布到了 CDN 使用的源站
多源站、对象存储复制、灰度发布和容器滚动更新都会造成“有的源站有文件,有的没有”。检查 CDN 配置中的源站地址、回源端口、协议、Host 和路径前缀,再逐个验证源站。
对象存储还要区分“不存在”和“无权访问”。有些存储服务会对未授权请求返回 403,有些代理规则会把 403 改写为 404。建议同时检查:
对象键是否与请求路径完全一致;
CDN 的源站身份或签名是否仍有效;
私有桶策略是否允许当前 CDN 读取;
回源路径是否重复拼接目录,例如
/static/static/app.js;最近是否切换了 Bucket、区域或源站域名。
安全地清除错误缓存
确认源站能够稳定返回 200 后,再清除对应 URL。优先做单文件刷新,范围清楚,也便于观察结果。若缓存键包含查询参数,需要按平台规则同时刷新对应变体;若大量资源都受影响,再考虑目录刷新或全站刷新。
刷新前保留一份响应头,刷新后从不同网络再次测试:
curl -sS -D before.txt -o /dev/null https://static.example.com/assets/app.js # 在 CDN 控制台刷新该 URL curl -sS -D after.txt -o /dev/null https://static.example.com/assets/app.js
比较状态码、Age 和缓存状态字段。首次请求可能显示 MISS,随后请求应返回 200,并逐渐变成 HIT。若刷新后仍是 404,继续查源站选择和缓存键,不要连续执行全站刷新。
调整错误缓存时间时要留出回滚路径
频繁发布新文件的站点,可以缩短 404 的缓存时间,或让特定资源目录的错误响应不被长期保留。调整前先记录原规则,只改一个测试目录,确认回源量和错误率没有异常后再扩大范围。
Nginx 反向代理若配置了错误响应缓存,也要一起检查:
proxy_cache_valid 200 10m; proxy_cache_valid 404 30s;
这段配置表示 200 缓存 10 分钟、404 缓存 30 秒。实际值要按发布频率和回源承载能力确定。把 404 完全设为不缓存会增加无效请求对源站的压力,尤其是扫描器、爬虫或错误链接较多的网站。
修改 Nginx 前先备份配置并检查语法:
sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak sudo nginx -t sudo systemctl reload nginx
如果错误率或回源量上升,可以恢复备份配置并重新加载。
用发布流程减少旧 404
静态资源发布应先上传带版本号或内容哈希的新文件,确认 CDN 路径返回 200,再更新 HTML 或接口中的引用。旧文件保留一段时间,避免仍在缓存中的页面引用已经删除的资源。
发布完成后可以自动探测一组核心 URL,记录 CDN 和源站状态码。监控中同时观察 404 比例、回源请求量和缓存命中率。这样能区分文件漏传、权限错误、节点旧缓存和路径重写问题,减少依赖全站刷新处理局部故障。
排查这类问题时,最有用的证据是同一 URL 在 CDN 与源站上的响应差异。源站稳定返回 200、缓存键确认一致、单文件刷新后节点重新取回成功,故障才算处理完成。