404 not found字面意思是服务器收到了请求,但找不到对应资源;在排障场景里,它更常被当成一条链路信号,而不是单一故障。当你修好一个404,却冒出另一类异常时,通常不是修错了,而是被修复对象的下游依赖被暴露出来。拆依赖链的目标,是判断该继续修,还是先回退。
假设一个栏目页原本返回404,你把它指向了新的聚合页。结果旧页面恢复可访问,但站内若干内链开始报错,日志里出现新的404路径。表面看是修复制造了问题,实际是原先被404掩盖的依赖关系开始显形。
这种矛盾通常有两种解释。第一种是依赖未同步:被修复页面的URL结构、参数或跳转目标变了,而引用它的模板、导航或站点地图仍按旧规则生成链接。第二种是异常被转移:原来的404把访问挡在某一层,修复后请求进入下一层,下一层本来就缺少对应资源,于是暴露出新的404。
两者都会表现为“修一处、坏一片”,但处理方向不同:前者要改引用方,后者要补下一层资源。
能区分解释的证据是请求进入的层级和引用来源。如果新异常集中在引用被修复页面的模板、导航或站点地图中,且这些链接指向的路径与被修复页面的新规则不一致,更符合依赖未同步。如果新异常出现在被修复页面自身展开后的子请求、接口或分页上,且这些资源原本就不存在,更符合异常被转移。
一个可操作的区分动作:先不改任何页面,只记录修复前后同一路径的请求链。修复前,请求在入口层就返回404;修复后,请求进入下一层并触发新的404。若新404的引用来源指向旧规则,先处理引用方;若引用来源正确但目标缺失,先补目标资源。这个动作的结果决定下一步是改链接还是建资源,而不是继续盲目替换URL。
拆链时不要从全站开始扫,而要从被修复对象出发,按依赖方向逐层检查。
检查顺序的意义在于:先确认被修复对象是否稳定,再确认引用方是否一致,最后确认下游资源是否齐全。顺序颠倒会把下游缺失误判为修复失败。
继续修的前提是:新异常属于依赖未同步,且引用方可以随修复一起更新。此时回退只会把问题重新藏回404后面,下一次修复仍会遇到同一批依赖。继续修的动作是同步更新引用方,并验证修复后请求能完整走完下一层。
先回退的前提是:新异常属于异常被转移,且下一层资源缺失范围不明或短期无法补齐。此时保留原404比暴露一批新错误更可控。回退后应记录被修复对象展开后依赖了哪些资源,等资源就绪再重新修复。这个判断条件比“哪个错误更少”更可靠,因为它指向的是依赖是否可控。
如果两种条件同时出现,优先处理依赖未同步,因为引用方通常比下游资源更容易收敛。
假设某栏目页原本返回404,你把它重定向到新聚合页。修复后栏目页可访问,但该页的分页链接开始返回404。检查发现,分页链接由模板按旧路径规则生成,而新聚合页并不存在这些分页。这里同时存在依赖未同步和异常被转移:模板按旧规则生成链接,新聚合页没有对应分页资源。
处理时先改模板的链接规则,让分页指向实际存在的路径;若实际路径不存在,再决定是否为聚合页补分页,或让分页链接不再输出。这个例子的数字和路径均为假设,仅用于说明比较方法:先看引用规则,再看目标资源,最后决定改哪一端。
验证时不要只看被修复页面是否返回200。还要看它展开后的子请求、引用它的页面、以及外部引用是否都指向一致的目标。若修复后请求量或抓取量出现变化,不能单独证明处理正确,因为缓存、跳转规则、外部链接更新延迟都可能带来同样现象。
更可靠的验证是:同一路径在修复前后分别请求一次,记录状态码、跳转目标和下一层请求结果。若修复后请求能完整走完依赖链且不再产生新的404,说明依赖已收敛;若只是把404从入口推到了下一层,说明拆链还没完成,下一步应继续向下检查,而不是宣布修复结束。