先别急着回滚。更稳的做法是把“修复动作”和“它所依赖的上游条件”分开列,再判断异常是修复本身造成的,还是上游条件在修复后暴露出来的。判断依据不是异常出现的时间先后,而是:去掉某一层依赖后,异常是否仍然存在。
404页面SEO的链条通常有两层。第一层决定“这个URL到底返回什么状态”:路由规则、重写规则、后端控制器、CDN或反向代理的拦截。第二层决定“返回404之后用户和爬虫看到什么”:404模板、状态码是否被覆盖、跳转逻辑、缓存策略、日志记录。
修复一层时动了另一层的依赖,就会出现“原来的问题好了,新问题来了”。例如你把软404改成真404,状态码正确了,但模板里依赖的一个接口在404上下文下不再被调用,页面直接空白;或者你给404加了跳转,状态码从404变成了301/302,原来的404统计归零,但这不是修复成功,而是问题被换了一种形式。
优先隔离状态码层。动作是:临时让404路径返回一个不含任何业务逻辑的最小响应,只保留状态码,观察异常是否消失。如果消失,说明问题在模板或模板依赖的数据;如果不消失,说明问题在路由、重写或代理层。
这个动作的结果直接决定下一步:状态码层干净,就去查模板依赖;状态码层仍异常,就不要继续改模板,先查上游规则。代价是这段时间404页面体验很差,所以只适合短时排查,不适合长期保留。
优先隔离模板层。动作是:把404模板替换成一个静态占位页,不调用任何接口、不读取任何动态数据,再观察异常是否复现。如果复现,问题多半不在模板,而在更外层的缓存、日志采集或统计脚本;如果不复现,再逐项加回模板依赖,每次只加一项。
这种逐项加回的方式比一次性恢复模板更慢,但能定位到具体是哪一项依赖被修复动作改变了。代价是需要多次发布或多次刷新缓存,适合有灰度或可快速回滚的环境。
每一步的结果都要写下来,因为下一步该动哪里,取决于上一步异常是否复现,而不是取决于哪一层看起来更可疑。
有些异常不是依赖链断裂,而是观测方式变了。比如你把软404改成真404后,统计里的404数量上升,这不一定代表错误变多,可能只是以前被当成200记录的页面现在被正确归类。反过来,404统计归零也不能单独证明处理正确,它可能是跳转把404变成了301/302,也可能是缓存仍在返回旧响应。
还要注意,robots.txt的抓取限制不等于可靠的索引移除,站点地图也不保证收录。修复404时如果顺手加了这些配置,异常可能来自它们,而不是404本身。不同搜索引擎对404、410和跳转的处理支持情况需要分别核查,不能用一个平台的表现推断另一个平台。
假设一个场景:某站把404模板从“带推荐内容的动态页”改成“纯静态提示页”,状态码正确了,但日志里开始出现大量模板渲染错误。此时先别回滚模板,而应检查日志采集是否还在按旧模板的字段解析。如果采集脚本依赖旧模板里的某个标记,模板一换,采集就报错。这种情况下,异常在采集层,不在404层。动作是修正采集规则,而不是把404模板改回去。
如果拆到某一层后,异常消失且修复目标仍达成,就继续沿这条链往下拆。如果拆到某一层后,修复目标失效、异常也没消失,说明这次修复和异常可能只是同时发生,应回到修复前的完整状态,重新确认异常的真实起点。
回滚不是失败,它是把依赖链恢复到已知状态,以便重新做一次只动一层的验证。真正要避免的是在两层同时改动的情况下反复试错,那样每次观察到的结果都无法归因。