bom头是php中“headers already sent”错误最隐蔽的元凶;它在文件开头插入ef bb bf三个不可见字节,被php视为实际输出,导致header()、setcookie()、session_start()失败。

确认错误是否由BOM头触发
UTF-8 BOM是PHP中“Headers already sent”最隐蔽的元凶之一。它在文件开头插入EF BB BF三个不可见字节,PHP会把它当作实际输出,导致header()、setcookie()、session_start()全部失败。
检查方法很简单:用VS Code、PhpStorm或Notepad++打开PHP文件,查看右下角编码显示。如果写着“UTF-8 with BOM”,立刻转为“UTF-8 without BOM”并保存。
- Windows记事本默认带BOM,绝对不要用它编辑PHP文件
- 宝塔面板自带编辑器、PHPStorm默认不写BOM,相对安全
- 线上环境(如共享主机)对BOM零容忍,本地测试通过不等于上线不出错
检查文件开头和结尾的空白字符
哪怕只是<?php 前多了一个空格、回车,或者?>后多了一行空行,都会触发该错误。PHP把它们全算作“已输出”。
实操建议:
- 打开文件,把光标移到第一列,按
Home键后直接敲End——如果光标没跳到<?php末尾,说明前面有隐藏字符 - 纯PHP文件(无HTML混排)建议省略
?>闭合标签,彻底规避结尾换行问题 - 用
cat -A filename.php(Linux/macOS)或Notepad++的“显示所有字符”功能,直观看到^M(回车)、^I(制表符)等
用headers_sent()定位真实输出位置
错误提示里写的行号常不准,尤其当文件被include或require时。真正输出可能来自被包含的配置文件、函数库甚至日志工具。
在出错前加这段诊断代码:
if (headers_sent($file, $line)) {
echo "Headers already sent in <code>$file</code> on line <code>$line</code>";
exit;
}
它能精准告诉你哪一行、哪个文件先动了输出——比看报错日志快得多。
- 这个函数返回
true就代表HTTP头已发出,不能再调用header() - 注意:
var_dump()、print_r()、error_log()(若配置了log_errors = On且error_log指向stdout)也可能触发输出
启用输出缓冲不是万能解药
ob_start()确实能让header()“看起来”更宽容,但它掩盖了根本问题,还可能带来副作用。
使用前必须确认:
- 必须放在脚本最顶部,
<?php之后立即执行,不能有任何前置语句或空行 - 避免多次嵌套调用
ob_start(),否则ob_end_flush()可能只清最内层缓冲 - 开启
zlib.output_compression = On或output_handler = ob_gzhandler时,ob_start()行为会异常,优先关掉这两个配置 - 云函数(如AWS Lambda + Bref)不支持传统SAPI输出缓冲,
ob_start()无效
真正难缠的,是那些跨文件、跨框架、被自动加载器悄悄引入的输出——它们不会因为开了缓冲就消失,只是延迟暴露。定位源头,永远比兜底更可靠。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











