Warning: Cannot modify header information - headers already sent by... 是 PHP 開發者最常遇到的錯誤之一。本文深度解析根因並提供多層次解決方案。
HTTP 協議規定:響應頭必須在響應體之前發送,且頭部發送後不可修改。任何在
setcookie()/header()/session_start()前的輸出(包括空白字符、BOM、錯誤信息、HTML)都會導致頭部提前發送。
<?php 前有空行/空格/BOM?> 後空行)Notice、Deprecated)
4. echo/print/var_dump 在頭部函數前調用
5. HTML 代碼混雜在 PHP 邏輯之前
output_buffering = On
; 或設置具體大小(字節),如 4096
output_buffering = 4096
重啟 PHP-FPM/Apache 生效。所有腳本自動緩衝,頭部函數可在任意位置調用(直到緩衝區刷新)。
<?php
ob_start(); // 腳本最頂部,任何輸出前
// ... 邏輯代碼 ...
setcookie('name', 'value'); // 即使前面有 echo 也可工作
ob_end_flush(); // 可選,腳本結束自動刷新
?>
<?php
// 登錄設置 Cookie 後重定向,避免同頁輸出衝突
setcookie('auth', $token, time()+3600, '/', '', true, true);
header('Location: /dashboard.php');
exit;
?>
<?php
// 臨時添加在報錯行前,輸出已發送頭部信息
headers_list(); // 返回已發送/準備發送的頭部數組
debug_print_backtrace(); // 追溯調用堆棧
?>
或啟用 output_buffering 後,在報錯行前 var_dump(headers_sent($file, $line)) 獲取具體發送文件與行號。
| 方案 | 優點 | 缺點 | 推薦度 |
|---|---|---|---|
| output_buffering=On | 零代碼修改、全局生效、兼容舊代碼 | 微小內存開銷、掩蓋架構問題 | ⭐⭐⭐⭐ |
| ob_start() 代碼控制 | 精確控制、無全局副作用 | 需修改代碼、易遺漏 | ⭐⭐⭐ |
| 代碼規範重構 | 根治、架構清晰、利於維護 | 工作量大、需團隊執行力 | ⭐⭐⭐⭐⭐ |
建議:開發環境啟用 output_buffering 快速開發,生產環境以代碼規範為主、緩衝為輔。