唯一责任方应当是“最终把响应写进HTTP状态行的那一层”,而不是生成链接或改写路径的任意系统。多系统并行时,先画出请求经过的顺序,再把404notfound的判定权收归到最靠近响应输出的一个组件;其余系统只负责声明意图,不直接决定状态码。
两种常见局面需要不同处理。第一种是链接生成冲突:CMS、路由框架、CDN回源规则各自改写路径,导致同一资源出现多个候选地址。第二种是响应冲突:请求已经到达源站,但应用路由、反向代理重写、边缘规则都可能返回404notfound。区分方法很简单——在源站入口记录一次原始请求路径,再记录应用路由匹配后的路径。如果两者一致却仍返回404,问题在响应侧;如果路径在到达应用前已被改写,问题在生成侧。
选择依据是:生成侧冲突适合用“单一链接生成器”收敛,响应侧冲突适合用“单一状态码裁决层”收敛。代价不同——前者要改模板和调用方,影响面广但长期稳定;后者只改一处中间件,见效快,但若上游仍持续产生错误路径,404notfound会反复出现。
如果服务器和框架都在自己手里,推荐让应用路由成为唯一责任方。具体动作是:在反向代理层关闭“找不到文件即返回404”的默认行为,改为把所有未命中请求转发给应用入口;应用入口用一个统一的兜底处理器决定返回404notfound还是重定向。这样做的结果是,代理配置变更不再悄悄改变状态码,排查时只需看应用日志一处。
例外是静态资源目录。若大量图片、脚本由Web服务器直接服务,可保留文件系统判定,但要在代理层显式声明哪些前缀走静态、哪些走应用,避免两套规则对同一路径都做判定。
当CDN或托管平台的边缘规则无法完全关闭,责任方应定义为“边缘层拥有最终否决权,但必须记录否决原因”。实施动作是:源站在响应头中附带一个内部标记,说明该路径应被视为存在还是不存在;边缘层只依据这个标记决定是否改写为404notfound,不再自行推断。结果是,边缘缓存和回源行为变得可预测,源站也能通过日志反查是哪一层改写了状态。
这种做法的代价是增加一次头部传递和解析。若平台不支持自定义响应头透传,就只能退回到条件一,把边缘规则降到最少,并接受一定的配置耦合。
假设请求依次经过:链接生成器 → 边缘重写 → 应用路由 → 静态目录。可以构造一个测试路径,让它只在一个环节被改写,观察最终状态码由谁决定。若同一路径在两次部署后返回不同结果,而代码未变,说明责任方不唯一,需要把判定逻辑上移到更靠近输出的那一层。这个验证不依赖具体平台,只需要能记录每层的输入输出。
需要提醒的是,抓取量或404notfound日志数量下降,并不能单独证明责任已经收敛——缓存命中、爬虫减少、监控采样变化都可能造成同样现象。应结合“同一路径在不同层是否得到一致判定”来判断。
完成这些步骤后,404notfound的责任归属就从“多方都可能”变成“一处可查”,后续无论是修链接还是调路由,都能先定位到唯一的那一层再决定下一步。