header("location: ...") 必须在任何输出前调用,包括session_start()、http状态码设置、bom字符等;location值需为绝对url;跳转后须exit()终止脚本;ob_start()仅掩盖问题而非解决。

header("Location: ...") 之前不能有 HTTP 头已发送的痕迹
不是只有 echo、print 或 HTML 才算“输出”——只要 PHP 已经把响应头(HTTP headers)发出去,header() 就必然失败。这包括:
-
session_start()成功执行后会隐式发送Set-Cookie头,之后再调用header("Location:...")就会报错 -
http_response_code(404)、status_header()(WP 环境)等函数也会提前发头 - 某些框架(如 Laravel 的
response()->redirect())底层已封装并触发了 header 发送,再手动调用header()会冲突 - PHP 启动时若开启
output_buffering=Off,且脚本开头有 BOM(\uFEFF)、UTF-8 带签名文件,BOM 会被当成“输出”,导致 header 失效
Location 值必须是绝对 URL(PHP 8.1+ 强制)
PHP 8.1 开始,header("Location: login.php") 这种相对路径写法会触发警告,并在某些 SAPI(如 CLI Web Server)下直接失效;浏览器也可能拒绝跳转。
- 正确写法必须是完整协议+域名:
header("Location: https://example.com/login.php") - 若需动态拼接,建议用
$_SERVER['REQUEST_SCHEME'] . "://" . $_SERVER['HTTP_HOST'] . "/login.php" - 注意:
$_SERVER['HTTP_HOST']可被客户端伪造,生产环境应白名单校验或使用配置中的可信域名
header() 后不 exit() 会导致逻辑泄漏和安全风险
header() 不终止脚本执行,后续 PHP 代码照常运行——哪怕浏览器已跳走。这不只是“多执行几行”的问题:
- 敏感操作(如数据库删用户、生成 token)可能在跳转后仍被执行
- 如果跳转后还
echo了 JSON 或 HTML,这些内容会被浏览器收到但不渲染(抓包可见),可能暴露调试信息或 CSRF token - 部分 CDN 或反向代理(如 Nginx + fastcgi_finish_request)可能忽略未显式结束的响应,导致响应体截断
- 标准做法是:
header("Location: /success"); exit;或die();(二者等效)
ob_start() 不是万能解药,它掩盖了架构问题
用 ob_start() 确实能绕过“已有输出”的报错,但容易让人忽视根本问题:
- 它只是把输出暂存到内存/文件缓冲区,直到
ob_end_flush()或脚本结束才真正发送——这意味着你本该早做的跳转,现在延迟到了整个页面逻辑跑完之后 - 若页面逻辑耗时长(比如查库、调 API),用户会卡住几秒才跳转,体验差且易重复提交
- 缓冲区大小有限(默认 4KB),若中间
echo太多,ob_start()会 silently 失败或触发Warning: ob_start(): failed to create buffer - 真实项目中,应该把跳转逻辑前置(如验证通过立即跳),而不是靠输出缓冲“兜底”
最常被忽略的一点:header 跳转依赖客户端行为,而浏览器对 Location 响应头的处理并不总是一致——比如 iOS Safari 对带空格或编码错误的 URL 会静默失败,不报错也不跳转。所以别只盯着 PHP 报错,跳转异常时先看 Network 面板里响应头是否真的发出了、值是否合法。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











