HTTP 緩存控制頭詳解
Cache-Control 指令對照表
| 指令 | 含義 | 典型場景 |
| public | 允許所有緩存(含 CDN/代理)存儲 | 靜態資源、公共 API |
| private | 僅瀏覽器私有緩存,代理不緩存 | 用戶個人頁面、含 Cookie 頁面 |
| no-cache | 緩存前必須向源服務器驗證(ETag/Last-Modified) | 頻繁更新的動態頁面 |
| no-store | 絕對禁止任何緩存 | 敏感數據、支付頁面 |
| max-age=秒 | 緩存有效期(相對時間) | 靜態資源長期緩存 |
| must-revalidate | 過期後必須驗證,不可使用過期副本 | 關鍵業務數據 |
不同操作下的緩存行為
| 用戶操作 | private/no-cache/must-revalidate | max-age=300 | no-store |
| 打開新窗口 | 重新請求服務器 | 5 分鐘內不請求 | 每次請求 |
| 地址欄回車 | 首次請求,後續不請求 | 過期前不請求 | 每次請求 |
| 點擊後退 | 不請求(讀緩存) | 不請求 | 每次請求 |
| 點擊刷新 | 強制請求 | 強制請求 | 強制請求 |
禁止緩存的標準組合拳
<?php
// 兼容 HTTP/1.0 + HTTP/1.1 + HTTPS
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
header('Cache-Control: post-check=0, pre-check=0', false); // 兼容舊 IE
header('Pragma: no-cache'); // HTTP/1.0 兼容
header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // 過去時間
?>
常用 header() 實戰模式
1. 301/302 重定向
<?php
// 永久重定向(SEO 傳遞權重)
header('HTTP/1.1 301 Moved Permanently');
header('Location: https://new-domain.com/path');
exit;
// 臨時重定向
header('Location: /login.php', true, 302);
exit;
?>
2. 文件下載響應頭
<?php
$filename = 'report-2024.xlsx';
$filepath = '/data/exports/' . $filename;
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment; filename="' . rawurlencode($filename) . '"');
header('Content-Length: ' . filesize($filepath));
header('Cache-Control: no-cache, must-revalidate');
header('Pragma: public'); // 兼容 IE 下載
readfile($filepath);
exit;
?>
3. JSON API 統一輸出
<?php
header('Content-Type: application/json; charset=utf-8');
header('X-Content-Type-Options: nosniff'); // 防 MIME 嗅探
header('Access-Control-Allow-Origin: *'); // CORS(生產環境請限制具體域名)
echo json_encode(['code' => 0, 'data' => $result], JSON_UNESCAPED_UNICODE);
?>
4. 狀態碼語義化輸出
| 狀態碼 | 用途 | PHP 寫法 |
| 200 | 成功 | header('HTTP/1.1 200 OK') |
| 201 | 創建成功 | http_response_code(201) |
| 400 | 參數錯誤 | http_response_code(400) |
| 401 | 未認證 | header('WWW-Authenticate: Bearer') + 401 |
| 403 | 無權限 | http_response_code(403) |
| 404 | 資源不存在 | http_response_code(404) |
| 429 | 請求過於頻繁 | header('Retry-After: 60') + 429 |
| 500 | 服務器錯誤 | http_response_code(500) |
header() 三大鐵律
- 必須在任何輸出之前調用(含空白字符、BOM、echo、HTML)
- 同名頭部後發送會覆蓋前者,除非第二參數
false 允許多值
- Location 頭部會自動觸發 302,除非顯式設置 301/303/307
常見坑與解決
<?php
// ❌ 錯誤:前面有空行/BOM
header('Location: /');
// ✅ 正確:ob_start() 緩衝輸出
ob_start();
echo '一些內容';
header('Location: /'); // 此時仍可發送頭部
ob_end_flush();
?>
掌握 header() 機制,是構建高性能、安全、標準化 Web 應用的基石。