header() 是 PHP 與 HTTP 協議溝通的核心橋樑。掌握它,就能精準控制瀏覽器行為:從編碼宣告、內容類型、快取策略、檔案下載,到 RESTful API 狀態碼回應。本文系統整理高頻用法,附帶最佳實踐與避坑指南。
<?php
// ❌ 錯誤:前面有空白/換行/BOM
header('Content-Type: application/json');
// ✅ 正確:腳本最頂端,無任何輸出
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['status' => 'ok']);
// 輸出結束後不再有任何內容
?>
違反會拋出 Cannot modify header information - headers already sent 警告並導致 Header 失效。
| 場景 | Header 範例 |
|---|---|
| HTML (UTF-8) | header('Content-Type: text/html; charset=utf-8'); |
| JSON API | header('Content-Type: application/json; charset=utf-8'); |
| XML | header('Content-Type: application/xml; charset=utf-8'); |
| CSV 下載 | header('Content-Type: text/csv; charset=utf-8'); |
| 圖片輸出 | header('Content-Type: image/jpeg'); / image/png |
| PDF 文件 | header('Content-Type: application/pdf'); |
| ZIP 打包 | header('Content-Type: application/zip'); |
<?php
// 200 成功(預設,通常可省略)
header('HTTP/1.1 200 OK');
// 301 永久重定向(SEO 遷移必備)
header('HTTP/1.1 301 Moved Permanently');
header('Location: https://new-domain.com/path');
exit();
// 302 暫時重定向
header('HTTP/1.1 302 Found');
header('Location: /login.php');
exit();
// 304 未修改(配合快取驗證)
header('HTTP/1.1 304 Not Modified');
exit();
// 400 錯誤請求
header('HTTP/1.1 400 Bad Request');
exit(json_encode(['error' => 'Invalid parameters']));
// 401 未授權(觸發瀏覽器登入彈窗)
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: Basic realm="Admin Area"');
exit('Access denied');
// 403 禁止存取
header('HTTP/1.1 403 Forbidden');
exit('Permission denied');
// 404 找不到
header('HTTP/1.1 404 Not Found');
exit('Page not found');
// 500 伺服器錯誤
header('HTTP/1.1 500 Internal Server Error');
exit('Server error');
<?php
// 立即重定向
header('Location: https://example.com/');
exit();
// 延遲 5 秒跳轉(瀏覽器支援度較好)
header('Refresh: 5; url=https://example.com/');
echo '5 秒後自動跳轉...';
注意: Location 值建議使用絕對 URL;相對路徑在某些舊瀏覽器表現不一。
<?php
$filePath = '/data/exports/report-2024.xlsx';
$fileName = '年度報表.xlsx';
if (!is_file($filePath)) {
header('HTTP/1.1 404 Not Found');
exit('檔案不存在');
}
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . rawurlencode($fileName) . '"');
header('Content-Length: ' . filesize($filePath));
header('Content-Transfer-Encoding: binary');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Expires: 0');
readfile($filePath); // 直接輸出檔案流,不佔用 PHP 記憶體
exit();
關鍵點:
rawurlencode() 處理中文檔名,避免瀏覽器亂碼。readfile() 直接將檔案流輸出至輸出緩衝,大檔案不會撐爆 memory_limit。X-Accel-Redirect (X-Sendfile) 讓 Nginx 直接傳檔,效能更高。| 目標 | Header 組合 |
|---|---|
| 完全禁止快取(動態 API、敏感頁面) |
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
| 允許快取 1 小時(靜態資源、公開 API) |
header('Cache-Control: public, max-age=3600');
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 3600) . ' GMT');
|
| 協商快取(需配合 ETag/Last-Modified) |
header('Cache-Control: no-cache, must-revalidate');
header('ETag: "' . md5($content) . '"');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $mtime) . ' GMT');
|
header('X-Powered-By: ');(或 php.ini 設 expose_php = Off)header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Methods: GET, POST, OPTIONS'); header('Access-Control-Allow-Headers: Content-Type, Authorization');header("Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com;");header('X-Frame-Options: DENY'); 或 SAMEORIGINheader('X-Content-Type-Options: nosniff');header() 覆蓋: 同名 Header 後者覆蓋前者,若需多值(如多個 Set-Cookie)使用 header('Set-Cookie: ...', false) 第二參數 false 表示不替換。ob_start() 可暫存輸出,但在 Header 發送前務必 ob_end_clean() 或 ob_end_flush()。curl -I url。header() 是 PHP 開發者與 HTTP 協議對話的鑰匙。熟練掌握內容類型、狀態碼、快取、下載、安全 Header 的組合拳,能讓你的 API 更標準、頁面更安全、檔案傳輸更可靠。記住:無輸出前發送、善用 exit()、驗證實際回應,是避坑三寶。