先给结论:当同一批死链检查在不同节点得到不同结果时,不要急着认定哪一次是错的,而要先确认每个检查点是否绕过了全部缓存层,再比较各层返回的状态码与响应头。大多数“时好时坏”的死链报告,根源是检查请求命中了不同缓存版本,而不是链接本身在两种状态之间反复跳变。
两种情况的证据形态不同,处理顺序也应不同。
如果问题出在缓存版本,你会看到:同一 URL 在同一分钟内被不同节点请求,返回的状态码不一致,但响应头里的 Age、X-Cache、CF-Cache-Status 一类字段不同;或者带随机查询串请求时结果稳定,去掉查询串后结果开始漂移。这说明源站状态是一致的,是中间层保存了不同时期的副本。
如果链接状态本身在变,你会看到:绕过所有缓存直连源站,多次请求仍出现 200 与 404 交替,且响应体内容或长度也不同。这属于源站逻辑或数据问题,缓存只是把它放大成了“不同版本”。
选择依据很简单:先用一个已知会返回 404 的测试路径,分别从直连源站、经过 CDN、经过反向代理三层各请求几次。如果只有某一层结果异常,问题就锁定在该层;如果三层都不稳定,才回到源站排查。
常规做法是直接刷新页面看结果,但浏览器和中间缓存都会复用旧副本,无法区分层次。更可靠的动作是给每次检查加一个唯一查询串,并显式要求不使用本地缓存。
?probe=8f3a1c,让路径在每一层都被视为新资源。这个动作的直接结果是:你能把“链接是否真的失效”和“缓存是否返回了旧版本”拆成两个独立结论。只有前者成立时,才需要进入替换或跳转处理;后者成立时,要处理的是缓存失效策略,而不是链接本身。
把注意力放在几个可观察字段上,而不是凭刷新次数猜测。
Age 较大且状态码为 200,而直连源站为 404:说明中间层仍在提供过期副本,需要触发该路径的缓存清除。Cache-Control 为 public 且带较长 max-age:404 结果也可能被缓存,导致修复后仍显示失效,需要确认失效通知是否覆盖了错误状态码。Vary 字段包含 User-Agent 或 Accept-Encoding:不同检查工具会命中不同副本,这解释了为什么换一个工具结果就变了。需要提醒的是,抓取量或请求量突然归零,并不能单独证明死链已被正确处理。它也可能是检查任务中断、缓存整体过期、或该路径被临时屏蔽造成的。要结合源站日志和响应头一起判断。
假设某栏目页在 CDN 节点返回 200,在反向代理层返回 404,直连源站返回 404。此时可以推断:反向代理和源站已经认为该页失效,CDN 仍在提供旧副本。
下一步动作是只对 CDN 层执行该路径的缓存清除,然后重新用带随机参数的请求验证。若清除后三层一致返回 404,就可以确认死链成立,进入替换或 301 处理;若清除后 CDN 仍返回 200,则要检查 CDN 的回源规则是否指向了另一个源站,或者是否存在独立的边缘缓存规则覆盖了清除操作。
这个例子的数字仅为说明比较方法,不代表任何真实项目的表现。
有几种情况不能简单套用上面的结论。第一,站点使用多源站或灰度发布,不同源站本身返回不同状态,此时缓存只是暴露了源站分歧。第二,检查工具自身带缓存或遵循了 robots.txt 限制,导致部分请求根本没有到达目标层;抓取限制不等于索引移除,检查结果缺失也不能直接当作链接正常。第三,站点启用了按地区或设备返回不同内容的规则,不同节点命中不同逻辑,状态码差异未必来自缓存。
遇到这些例外,先确认检查请求是否真正到达了你想测的那一层,再决定是修缓存、修源站逻辑,还是调整检查方法。只有在确认请求路径和缓存键都一致之后,状态码的差异才适合被当作死链证据使用。