当两个地址返回完全相同的正文,却分别带着 404 与 200 响应头时,先不要按内容判断,而要把响应头当作对外的真实声明。正文相同只说明用户看到的东西一样,响应头不同则意味着抓取、索引和后续链接处理会走向两条路。多数情况下应保留正确的状态码并改写内容呈现,而不是为了“看起来正常”把 404 一律改成 200。
处理一个地址时,抓取端先拿到状态行和响应头,再决定是否解析正文。正文相同但状态码不同,等于同一段内容被贴上了两种身份标签。200 表示这个地址是一个正常可访问的资源,404 表示它不存在。两者不会因为页面里写了同样的推荐语或同样的导航而互相抵消。
这也解释了为什么“用户能打开”不能作为判断依据。浏览器对 404 页面照样会渲染正文,用户甚至看不出差别。真正被后续流程读取的是状态码、Content-Type、Cache-Control、X-Robots-Tag 以及可能存在的重定向头。只要状态码与你的真实意图不一致,后面所有基于该地址的判断都会偏。
当地址对应的资源确实已经不存在,且没有等价替代地址时,保留 404 是正确选择。此时可以改写正文,让页面说明发生了什么、给出站内搜索入口或返回栏目链接,但状态码不改。这样做的结果是:抓取端知道该地址失效,不会把它当作新内容;用户仍然得到有用的落地页。
需要留意的是,404 页面里的链接仍会被发现和跟进,所以不要把大量站内链接堆在错误页上,否则会稀释抓取预算,也可能让失效地址反复被访问。如果同一批地址在规模化后出现例外,例如部分地址其实已有新对应页,那就不属于保留 404 的场景,应转为下一节的处理。
改写适用于“地址变了、内容还在”的情况。此时应返回 301 指向新地址,而不是返回 200 再在正文里放一个跳转脚本。301 的结果是权重与用户请求都转移到新地址,旧地址不再作为独立页面参与判断。若新旧地址只是参数或大小写差异,也应统一到一个规范形式,避免同一内容出现多个可访问版本。
退出则适用于内容已被彻底删除、且没有替代页的情况。这里的退出不是把 404 改成 200 假装存在,而是接受该地址失效,并清理指向它的内部链接和站点地图条目。站点地图不保证收录,把失效地址留在站点地图里只会增加无效抓取。robots.txt 的抓取限制也不等于可靠的索引移除,它阻止的是抓取,不是已经形成的索引判断,因此不能用它替代 404 或 301。
个别样本成立、规模化后出现例外,通常来自三类原因。第一类是服务端路由或 CDN 规则不一致,同一批地址有的命中自定义错误页返回 404,有的被兜底规则接管返回 200。第二类是前端渲染接管了状态码,单页应用在客户端切换路由时,HTTP 状态早已在首次响应中确定,后续视图变化不会改写它。第三类是缓存层把某个 200 响应复用到了本应 404 的地址上。
区分这些原因可以看一组证据:同一地址直接请求源站与经过缓存层请求,状态码是否一致;关闭脚本后状态码是否变化;同一路径大小写或带尾斜杠时状态码是否漂移。如果只有经过缓存或只有开启脚本后才变成 200,问题在缓存或渲染层,不在内容本身。
假设某站有一批下架商品页,正文都换成了“该商品已下架”加推荐列表。若这些地址返回 404,抓取端会按失效处理;若返回 200,抓取端会当作正常页面,可能继续索引这段几乎相同的正文,形成大量近似页面。这里的数字只是说明比较方法:如果 100 个地址里有 30 个返回 200,这 30 个就会脱离失效处理流程。
可执行的动作是先抽查源站响应头,确认状态码与真实意图一致;再把确认失效的地址保留 404,把有替代页的地址改为 301,并同步更新内部链接与站点地图。做完这一步后,下一步应观察这些地址在后续抓取中的状态码是否稳定,而不是只看用户能否打开。若状态码仍在 200 与 404 之间漂移,优先排查缓存与渲染层,而不是继续改正文。