先给结论:当测试工具返回 200、实际用户却看到 404 时,最可能的遗漏条件是请求上下文不一致,而不是服务器没有配置 404。要复现,必须让测试请求携带与真实用户相同的条件——来源、路径大小写、查询串、Cookie、User-Agent、地区出口和协议版本。只改其中一项往往仍复现不了,因为触发条件可能是多项叠加。下面给出可操作的复现顺序,以及一个会让结论失效的反例。
很多排查一开始就错了:把“测试工具返回 200”当作基准,然后反复检查服务器配置。更有效的做法是先确认用户侧失败的原始记录。浏览器开发者工具的网络面板会显示实际请求的完整 URL、状态码、响应头和重定向链。如果用户是通过某个入口跳转过来的,还要记录跳转前的地址。
需要收集的最小字段包括:完整请求 URL(含查询串)、请求方法、Referer、User-Agent、Cookie 中与路由或会话相关的键、响应中的 Location 头、以及失败发生的时间戳。没有这些字段,后续复现只能靠猜。
一个实际动作:让遇到问题的用户打开开发者工具,刷新页面,把失败请求“复制为 cURL”。这个命令包含真实请求的全部头部。拿到它之后,下一步不是直接跑,而是先与测试工具的请求逐项对比。
复现的本质是控制变量。可以从一条能返回 200 的测试请求出发,逐项替换为用户的真实值,每替换一项就重跑一次,观察状态码何时从 200 变成 404。这个顺序能定位到具体哪一项触发了失败。
每次替换后记录状态码和响应头。如果替换到某一项时状态码变化,这一项就是候选触发条件;如果全部替换完仍是 200,说明遗漏条件不在请求本身,而在网络路径或时间维度上。
假设你把所有请求头、URL、Cookie 都对齐了,测试工具仍然返回 200,而用户持续 404。这时“请求上下文不一致”的解释就不成立,需要转向另外两类原因。
这两种情况下,继续调整请求头不会有效果,因为触发条件不在请求内容里。需要改为对比不同网络位置的响应,或按时间线核对变更。
测试工具通常从固定机房出口发起请求,不执行页面内的 JavaScript,也不携带用户浏览器自动附加的头部。实际用户请求会经过 DNS 解析、CDN 边缘、可能的重定向链,并在页面内发起后续的异步请求。404 可能出现在这些后续请求上,而不是首屏文档。
一个假设的例子:某页面首屏 HTML 返回 200,但页面内通过脚本请求 /api/v2/items,而实际用户请求的是 /api/V2/items。测试工具只检查了首屏 URL,因此显示正常。此时复现的关键是找到那个失败的具体子请求,而不是继续检查首屏。
因此,当测试工具显示正常时,先确认你检查的是不是用户实际失败的那条请求。如果不是,所有后续对比都没有意义。
一旦用某组条件稳定复现 404,下一步是确认这组条件在生产流量中是否普遍。可以在一段时间内记录带有该条件的请求比例,判断影响范围。如果条件只出现在少数用户身上,可能是特定运营商、地区或客户端版本的问题;如果普遍出现,则更可能是配置或路由规则本身的问题。
在修复前,不要只针对测试工具返回 200 就宣布问题解决。修复后需要用同一组复现条件重新验证,并确认原本失败的请求现在返回预期状态码。如果修复改变了路由规则,还要检查是否引入了新的重定向或循环。
最后,把复现条件记录下来。下次出现类似“工具正常、用户失败”的情况时,可以直接从这组条件开始验证,而不必重新逐项对比。