PHP header函数重定向异常:/old_url.html返回404却存在Location头
首先咱们抓住核心矛盾:浏览器只会在响应状态码为3xx系列(301/302/307/308)时,才会遵循Location头执行重定向。如果返回的是404状态码,哪怕Location头存在,浏览器也会直接忽略它,显示404页面。结合你的描述,我梳理了几个最可能的原因:
1. Web服务器静态文件优先级高于PHP处理(最常见)
大多数Web服务器(Apache/Nginx)默认会优先尝试匹配请求的静态文件。当你请求/old_url.html时,服务器会先去网站根目录找这个.html文件,发现不存在后,很多默认配置会直接返回404,根本没把请求交给你的PHP重定向逻辑处理。
你提到响应头里有Location,说明PHP确实执行了,但可能你的服务器配置存在冲突——比如部分规则把.html请求导向PHP,同时另一部分规则又因为文件不存在返回404,导致状态码被覆盖。
解决方法:
调整服务器配置,确保所有不存在的静态文件请求都转发到PHP处理:
- 对于Apache,修改
.htaccess:RewriteEngine On # 如果请求的文件/目录不存在,转发到index.php RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php?$1 [L,QSA] - 对于Nginx,修改站点配置:
location / { try_files $uri $uri/ /index.php?$query_string; }
2. 重定向状态码配置错误
虽然/old_url正常,但/old_url.html对应的$query[0]['status_code']可能不是3xx系列(比如误设为404)。这种情况下,PHP确实会发送Location头,但状态码是404,浏览器自然不会执行重定向。
解决方法:
在代码里添加调试,确认状态码:
// 在header()之前临时加入调试代码 var_dump($query[0]['status_code']); exit;
如果发现状态码不对,要么修正数据库里的配置,要么在代码里强制兜底为3xx:
if($query && !headers_sent()){ $status = (int)$query[0]['status_code']; // 确保状态码是合法的重定向码 if($status < 300 || $status >= 400){ $status = 301; // 默认用永久重定向 } header('Location: ' . $query[0]['new_url'], true, $status); exit; }
3. 服务器模块覆盖了状态码
某些服务器模块(比如Apache的mod_speling、Nginx的error_page配置)可能会因为文件不存在,把PHP发送的3xx状态码强制改成404。这种情况比较少见,但可以排查一下服务器的错误页配置。
解决方法:
检查服务器配置中是否有类似error_page 404 /404.html;的规则,暂时注释掉后测试,看是否能正常重定向。
快速排查步骤
- 用浏览器开发者工具的Network面板,查看
/old_url.html请求的状态码和响应头,确认状态码是否为3xx; - 验证服务器配置是否把
.html请求正确转发到PHP; - 检查
$query数据中/old_url.html对应的状态码是否合法。
内容的提问来源于stack exchange,提问作者nikksan

