如果你写 PHP 写着写着,突然冒出来这么一句:
Warning: Cannot modify header information - headers already sent by …
恭喜你,你踩到了 PHP 新手“最恶心”“最隐蔽”的坑之一。
它不像 Undefined variable 那种能看到明显的原因。
这个错非常阴险——页面能正常打开,但登录跳转、下载文件、接口返回全都崩掉,怎么弄都不行。
99% 的新手都会在这里卡半天。
今天这篇文章,我带你从零讲透:
- 这个错误到底是什么意思?
- 为啥连空格/换行都能触发?
- 怎么快速定位?
- 怎么彻底解决?(含 BOM 检查全步骤)
看完这篇,你看到这个错误能 10 秒锁定原因。
一、这个错误到底是什么意思?(超简单解释)
一句话:
你在发送 header 之前,PHP 已经输出内容了,所以 header 发不出去。
什么意思?
你执行 header()、setcookie()、session_start() 时
PHP 要发送 HTTP 响应头。
但如果你前面已经有任何输出:
- echo 内容
- HTML 字符
- 一个空格
- 一个换行
- 甚至是文件开头的 UTF-8 BOM
PHP 都会说:
“兄弟,你内容都输出了,我怎么再发 header?”
于是就报:
Cannot modify header information
二、为什么一个空格都能导致报错?
因为 PHP 认为 任何 输出都会影响 HTTP 头。
例如这里你看似没输出:
<?php
echo "hello";
header("Location: xxx.php");
但是这段你能理解。
重点是:
<?php
⏎ (你以为没东西,但其实有换行)
header("Location: xxx.php");
甚至:
[空格]<?php // 文件开头有一个空格
PHP 都认为你已经输出了内容。
输出一旦开始,就不能再发 header。
三、触发这个错误的五大元凶(每一个都很常见)
下面这 5 个原因基本覆盖 99% 的情况。
① 文件开头/结尾有空格、回车
最常见,没有之一。
尤其是多人协作项目、Windows 编辑器、从网上复制粘贴代码时。
你看上去没有,但文件里确实有空格。
② UTF-8 BOM(最隐蔽的元凶)
这个是 PHP 新手的终极噩梦。
某些编辑器保存文件时,会自动加 BOM:
三个 invisible 字节:EF BB BF
你肉眼看不到
PHP 却认为你“偷偷输出了三个字符”
于是 header 全爆。
③ 在 header() 之前 echo、print_r、var_dump
常见于调试代码忘删:
var_dump($data);
header("Location: /home.php");
这肯定报错。
④ include/require 的文件中偷偷输出内容
你在 A.php 使用 header
但 B.php 开头有空格
C.php 结尾有 HTML
都会导致 A.php 报错。
最难排查的情况:
自己写的文件没问题,是某个包含文件输出了内容……
⑤ 输出缓冲关闭(ob_flush / ob_end_clean)导致无法拦截输出
你本来开启了 ob_start,但中间不小心把缓冲清掉了。
四、如何快速定位错误?(3 步大法)
这是我总结给新手最有效的方法。
第一步:看错误信息中的“sent by”
报错一般长这样:
.... headers already sent by (output started at /path/test.php:23)
重点看:
output started at /xxx/xxx.php:23
这就是“第一次输出发生的地方”,直接去那一行找 空格 / echo / HTML / BOM。
第二步:用编辑器打开“显示不可见字符”
在 VSCode / Sublime / Notepad++ 里开启:
Show whitespace / Show BOM
你能看到:
- 文件开头是否有 BOM
- 空格
- Tab
- 换行
一目了然。
第三步:全项目搜索 var_dump / echo / print
尤其是以下情况:
- 某个接口莫名其妙 header 失效
- 没有明显 echo 却仍然报错
可能是别的文件偷偷输出了。
一搜就能定位。
五、终极解决方案(建议直接收藏)
下面的做法能从根源杜绝这个问题。
✔ 方案 1:彻底删除空格、换行
文件必须以:
<?php
开头
结尾也不应该有多余换行。
✔ 方案 2:必须保存成“UTF-8 无 BOM”
最关键。
VSCode 做法:
- 打开文件
- 点击右下角编码
- 选择“Save with encoding”
- 选:“UTF-8”
(不是 “UTF-8 with BOM”!!)
Notepad++ 做法:
【编码】→【转换为 UTF-8(无 BOM)】
✔ 方案 3:避免逻辑页面中夹杂 HTML
建议这样写:
❌ 错误写法:
echo "开始";
header("Location: index.php");
✔ 正确写法:
<?php
header("Location: index.php");
exit;
✔ 方案 4:开启输出缓冲(防止误输出)
放入口文件:
ob_start();
这样即使其他文件偷输出
也能被缓冲区拦截
直到你手动 flush。
✔ 方案 5:用框架(Laravel / ThinkPHP)来封装响应
框架层都帮你做了 header 封装
不会随便乱输出。
六、最关键的:BOM 如何彻底排查?(详细步骤)
这是很多文章都没讲清楚的地方
我给你总结到最细:
① 观察文件编码
VSCode 右下角:
- UTF-8(无 BOM) → OK
- UTF-8 with BOM → 异常
② 打开 Hex Viewer 插件
看前 3 个字节是否:
EF BB BF
有就是 BOM。
③ 用 Linux 命令行查(最精准)
hexdump -C filename.php | head
看到:
ef bb bf
直接确诊。
④ 批量移除 BOM(终极方案)
find . -type f -name "*.php" -exec sed -i '1s/^\xEF\xBB\xBF//' {} ;
整个项目一次性清干净。
七、总结一句话
Cannot modify header information = 你在 header 前输出了东西(包含不可见的 BOM)
排查顺序:
- 错误信息里的“sent by”文件
- 检查空格 / HTML / echo
- 检查 BOM
- 检查 include 文件
- 最终用 ob_start 兜底
照这个流程走
你 99% 能在 10 秒找到问题。