許多開發者遇過 Cannot modify header information - headers already sent 警告,但有時 echo 在 header() 之前卻不報錯。為什麼會有這種差異?關鍵在於 輸出緩衝區。
想像看影片時的「緩衝」:播放器不會立即播放下載的資料,而是先存入緩衝區,累積一定量才輸出畫面。PHP 執行腳本時亦同:輸出內容(如 echo)先寫入緩衝區,達到閾值或腳本結束才一次性發送給瀏覽器。
output_buffering = 4096 ; 預設 4KB
當輸出內容小於 4KB 時,資料留在緩衝區,header() 仍可插入 HTTP 標頭;若超過 4KB,緩衝區自動刷新,標頭已隨內容發出,後續 header() 便會觸發警告。
// 測試 1:預設 4KB 緩衝區
echo 'hello world!'; // 12 bytes < 4KB
header('Content-Type: text/html; charset=utf-8'); // 成功,不報錯
// 測試 2:將 output_buffering 改為 5 bytes
echo 'hello world!'; // 12 bytes > 5 bytes,緩衝區溢位刷新
header('Content-Type: text/html; charset=utf-8'); // 警告:headers already sent
echo 在前、header() 在後,若緩衝區未滿,實際網路封包順序仍是 Header 先、Body 後;一旦緩衝區滿溢提前 flush,Body 先發出,Header 隨後才到,違反 HTTP 協定,故報錯。| 函數 | 說明 |
|---|---|
| ob_start() | 開啟輸出緩衝 |
| ob_clean() | 清空緩衝區內容 |
| ob_get_contents() | 取得緩衝區內容字串 |
| ob_get_clean() | 取得內容並清空緩衝區 |
header()、session_start()、setcookie() 寫在腳本最頂端,任何輸出之前。ob_start() 強制開啟緩衝。理解「緩衝區大小」與「Header 發送時機」的關係,就能徹底避開 headers already sent 錯誤。開發時務必養成「先標頭、後內容」的編碼習慣,並善用 ob_start() 掌控輸出時序。