当静态响应显示404、而浏览器执行脚本后页面又能渲染出内容时,先不要急着改状态码。这个矛盾通常说明两件事之一:要么服务器对同一URL返回了不同状态,要么页面本身是软404。区分它们的关键证据,是查看原始响应头、对比脚本执行前后的DOM,并确认返回404的那次请求是否真的指向了目标URL。
静态响应与脚本渲染结果不同,指的是用不执行脚本的方式请求URL时得到404,而用浏览器或渲染服务执行脚本后却看到正常内容。这个差异可能出现在三个位置:HTTP状态码、响应正文、以及最终渲染出的可见内容。定位时要把这三者分开记录,不要用“页面能不能打开”这种混合判断。
实际操作上,先用只取响应头的方式请求目标URL,记录状态码和重定向链;再用支持脚本执行的方式请求同一URL,记录最终URL、状态码和渲染后的可见文本。两次请求的URL必须完全一致,包括末尾斜杠和查询参数,否则差异可能只是路由匹配不同造成的,与404处理无关。
解释一:服务端对同一路径做了条件分流。 服务器可能根据请求头、Cookie、User-Agent或Accept字段返回不同结果。例如对不带特定请求头的请求返回404,对带完整浏览器请求头的请求返回200。这种情况下,404是真实的服务端响应,脚本渲染只是拿到了另一条分支的内容。
解释二:页面是软404。 服务器对所有请求都返回200,正文里却由脚本判断内容不存在后渲染出“页面不存在”的提示。反过来也成立:服务器返回404,但脚本仍然把缓存的模板或默认内容渲染出来,让肉眼以为页面正常。软404的核心特征是状态码与页面语义不一致,而不是渲染结果本身。
这两种解释的修复方向完全不同。前者要改服务端的分流规则或缓存策略,后者要统一状态码与内容判断逻辑。搞错方向会导致改完之后矛盾依旧,只是换了一个表现位置。
最直接的证据是原始响应头。如果同一URL在不执行脚本时返回404、在执行脚本时返回200,且两次请求头差异明显,那更可能是服务端分流。如果两次都返回200,只是渲染文本不同,那更可能是软404。如果两次都返回404,但渲染后仍有可见内容,说明脚本在404响应上继续渲染了模板,属于状态码与内容脱节。
第二组证据是响应正文的长度和结构。服务端分流通常会让两次响应的正文完全不同,一次是错误页,一次是正常页;软404则往往共用同一套页面骨架,只是内容区域被替换。把两次响应的正文长度、是否包含同一模板标记、是否引用同一批静态资源列出来,就能看出它们是两条独立分支还是同一分支的不同渲染阶段。
第三组证据是请求是否携带了脚本执行环境特有的头或Cookie。可以逐项去掉这些差异再请求,观察404是否消失。如果去掉某一项后静态请求也返回200,就基本确认是服务端按请求特征分流,而不是页面内容真的不存在。
假设某URL在无脚本请求下返回404,在浏览器中却显示完整文章。先固定请求方法为GET,只改一个变量:是否带某个会话Cookie。如果带Cookie时返回200、不带时返回404,那么差异来自服务端分流,下一步应检查该Cookie对应的缓存或权限逻辑,而不是去改前端路由。反之,如果带不带Cookie都返回200,只是渲染文本不同,就应检查前端是否存在“内容为空时仍渲染默认模板”的分支,并把这种情况改为返回真实404状态。
这个例子的数字和条件都是假设,目的是说明比较方法:一次只改一个变量,看状态码是否随之改变。动作的结果直接决定下一步是查服务端还是查前端。
如果证据指向服务端分流,优先统一同一URL的状态码,避免同一路径对不同请求给出互相矛盾的信号。如果证据指向软404,优先让内容判断与状态码保持一致:内容不存在时返回404,内容存在时返回200,不要让脚本在404响应上继续渲染正常内容。
还要注意,robots.txt的抓取限制不等于可靠的索引移除,站点地图也不保证收录。即使状态码改对了,索引结果仍可能滞后或受其他因素影响,因此不要把状态码修复直接等同于索引问题解决。把状态码、渲染内容和索引结果分开记录,才能在下一次出现矛盾时快速判断差异出在哪一层。