PHP Warning: Cannot modify header information 终极避坑指南(含 BOM 彻查方法)

49 阅读4分钟

如果你写 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 做法:

  1. 打开文件
  2. 点击右下角编码
  3. 选择“Save with encoding”
  4. 选:“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)

排查顺序:

  1. 错误信息里的“sent by”文件
  2. 检查空格 / HTML / echo
  3. 检查 BOM
  4. 检查 include 文件
  5. 最终用 ob_start 兜底

照这个流程走
你 99% 能在 10 秒找到问题。