headers already sent 错误是因为在调用 header()、session_start() 或 setcookie() 等函数之前,php 已经向浏览器发送了输出内容。解决该问题的最直接方法是使用输出缓冲控制(output buffering),通过开启 ob_start() 来拦截并暂存所有输出,直到脚本执行完毕或手动刷新缓冲区时再统一发送 http 头信息。

为什么会出现 Headers already sent 错误
HTTP 协议规定响应头必须在响应体之前发送。一旦 PHP 引擎遇到任何非头信息的输出(如 HTML 标签、空格、换行符或 echo 语句),就会立即将已收集的头部信息和这些输出发送给客户端。此时如果代码后续尝试修改头部信息,就会触发致命错误。常见触发场景包括文件开头的 BOM 字节序标记、HTML 标签前的空白字符,或者在包含文件中意外输出的内容。

<?php
// 开启输出缓冲,防止早期输出导致头部发送失败
ob_start();
// 模拟一些早期输出,例如调试信息或 HTML 结构
echo "<html><body>";
// 此时设置 Cookie 或重定向依然有效,因为数据仍在缓冲区中
setcookie('user_id', '12345', time() + 3600);
header('Location: /dashboard');
// 输出剩余内容
echo "</body></html>";
// 可选:显式刷新缓冲区
ob_end_flush();
?>上述代码展示了如何通过 ob_start() 包裹整个逻辑。即使 echo 先于 setcookie 执行,由于输出被缓存,header 和 cookie 的设置仍然能在最终发送前完成。这种方式允许开发者在代码任意位置处理头部逻辑,而不必严格遵循“先逻辑后输出”的传统顺序。
立即学习“PHP免费学习笔记(深入)”;
如何排查具体的输出源头
当无法使用输出缓冲或需要修复根本原因时,必须定位具体是哪个字符导致了提前输出。PHP 提供了 headers_sent() 函数来检测头部是否已发送,结合 debug_backtrace() 可以精确定位出错的文件和行号。此外,检查文件编码是否为 UTF-8 without BOM 也是关键步骤,因为 BOM 会在文件最开头产生不可见的三个字节,直接导致头部发送失败。

<?php
if (!headers_sent($file, $line)) {
// 仅在头部未发送时执行操作
header('Content-Type: application/json');
echo json_encode(['status' => 'ok']);
} else {
// 记录错误日志以便排查
error_log("Headers already sent in {$file} on line {$line}");
echo json_encode(['error' => 'Headers already sent']);
}
? 这段代码演示了防御性编程的做法。通过 headers_sent() 获取报错的具体文件和行号,开发者可以快速定位到是某个 include 文件末尾多了一个换行,还是模板文件中混入了 PHP 标签外的文本。这种方法比单纯依赖输出缓冲更能帮助维护代码的规范性,避免隐藏潜在的格式问题。
基于 session_start 调用的具体问题分析
在使用 session_start() 时,如果页面顶部有空白字符,同样会引发此错误。这是因为 Session 机制依赖于 Set-Cookie 头部来维持用户状态。如果在调用 session_start() 之前有任何输出,服务器就无法发送 Session ID 的 Cookie,导致会话无法初始化。确保 <?php ?> 标签之间没有多余的空行,或者将 session_start() 放在脚本的最第一行,是解决此类问题的标准做法。



















