首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP "Cannot modify header information" 錯誤徹底解決指南

     編輯:PHP 綜合知識

錯誤本質

Warning: Cannot modify header information - headers already sent by (output started at ...)

原因:HTTP 協定規定 Header 必須在 Body 之前發送。任何輸出(echo、print、HTML、空白、BOM、錯誤訊息、var_dump)都會導致 PHP 發送 Header 結束標記並開始傳送 Body,此後再呼叫 header()、setcookie()、session_start() 即觸發警告。

六大常見觸發場景

場景範例檢查重點
檔案開頭有空白/BOM<?php 前有換行、空格、UTF-8 BOM用十六進位編輯器/IDE 確認檔案以 3C 3F 70 68 70 (
include/require 檔案輸出配置檔末尾有 ?> 後空白、錯誤訊息純 PHP 檔案省略結束標籤
錯誤/警告輸出Warning: mysqli_connect()... 在 header() 前開發環境 display_errors=On,生產 Off + 記錄日誌
隱含輸出var_dump()、print_r()、die() 字串除碼後移除或改用 logger
Session/cookie 在輸出後HTML 輸出後才 session_start()移至腳本最頂端
多重 include 鏈A.php include B.php,B.php 有輸出追蹤 output started at 指向的檔案:行號

四種解決方案(優先級排序)

1. 根治:消除輸出源頭(最佳)

  • 所有純 PHP 檔案刪除結束標籤 ?>
  • 檔案編碼統一 UTF-8 無 BOM
  • 入口檔最頂端無任何空白字元
  • 錯誤處理改用 try-catch + Logger,關閉 display_errors

2. 輸出緩衝:ob_start()(快速修復、舊專案適用)

<?php
// 入口檔最開頭(在任何輸出前)
ob_start(); // 開啟輸出緩衝,所有 echo 暫存記憶體

// ... 正常代碼 ...
echo '內容';
header('Location: success.php'); // 此時仍可修改 header
setcookie('token', $value);
session_start();

// 腳本結束時自動刷新緩衝,或手動
ob_end_flush();

注意:

  • ob_start() 會增加記憶體用量(大輸出時注意)
  • 巢狀 ob_start() 需配對 ob_end_flush()/clean()
  • 無法解決 已發送到瀏覽器 的輸出(如 flush()、Web Server 緩衝滿)

3. php.ini 設定 output_buffering(系統級、影響所有腳本)

; php.ini
output_buffering = 4096 ; 4KB 內部緩衝,或 On 表示無限
; 重啟 PHP-FPM/Apache 生效

優缺點:一勞永逸,但隱藏了架構問題(輸出順序錯誤),除錯時較難發現過早輸出。

4. 重構架構:分離邏輯與輸出(現代最佳實踐)

<?php
// Controller / Action
function loginAction(): void {
    // 1. 純邏輯:驗證、設定 Session、Cookie、資料庫
    if ($valid) {
        $_SESSION['user'] = $user;
        setcookie('remember', $token, time()+86400, '/', '', true, true);
        // 2. 回應物件(不直接輸出)
        return new RedirectResponse('/dashboard');
    }
    // 3. 失敗回應
    return new ViewResponse('login.php', ['error' => '帳號密碼錯誤']);
}

// Front Controller / Router
$response = $router->dispatch($request);
// 統一發送 Header + Body
$response->send();

採用 PSR-7 Response 介面(Guzzle, Symfony HttpFoundation, Laravel Response, Slim 等),徹底消除「何時輸出」的時序耦合。

除錯技巧

<?php
// 臨時加在懷疑位置,追蹤輸出來源
function debugOutputStarted(): void {
    $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10);
    error_log('Output started trace: ' . json_encode(array_map(fn($f) => [
        'file' => $f['file'] ?? 'unknown',
        'line' => $f['line'] ?? 0,
        'function' => $f['function'] ?? 'unknown',
    ], $trace)));
}

// 或使用 headers_sent() 檢查
if (headers_sent($file, $line)) {
    error_log("Headers already sent in $file:$line");
}

總結檢查清單

  1. 檢查 output started at 指向的檔案:行號
  2. 該檔案是否有 BOM/空白/HTML/echo/錯誤訊息?
  3. 是否被其他檔案 include 且該檔案有輸出?
  4. 是否在 header()/session_start()/setcookie() 前有輸出?
  5. 依優先級套用:修正源頭 → ob_start() → php.ini → 重構回應物件
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved