用戶提交表單 → 跳轉結果頁 → 點擊瀏覽器「後退」 → 表單頁面顯示但**所有輸入框為空**。這是經典的 **Session 阻止緩存** 問題。
session_start() 為防止敏感頁面被代理/瀏覽器緩存,**默認發送以下 Header**:
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Expires: Thu, 19 Nov 1981 08:52:00 GMT
瀏覽器收到 `no-store`,**不將頁面存入磁盤/內存緩存**。點擊後退時,瀏覽器無緩存可讀,重新請求服務器 → 服務器重新執行腳本 → 變量重置 → 表單為空。
| 方案 | 代碼 | 優點 | 缺點/風險 |
|---|---|---|---|
| 1. 私有緩存 (推薦) | session_start(); | 允許瀏覽器緩存**僅供當前用戶** (私有緩存),後退即時恢復表單數據;不泄露給代理/CDN。 | 共用電腦下其他用戶可能看到殘留數據 (極低風險)。 |
| 2. 允許緩存但驗證 | header('Cache-Control: private, max-age=0, must-revalidate'); | 瀏覽器緩存但每次後退發請求驗證 (304),數據仍由服務器生成。 | 增加服務器壓力,後退非即時。 |
| 3. 關閉 Session 緩存限制器 | session_cache_limiter('private'); | 官方 API,效果同方案 1,語義更清晰。 | 需在 session_start() **之前** 調用。 |
| 4. 表單數據本地存儲 (前端) | JS `localStorage` 保存草稿,頁面載入時恢復 | 徹底解決,跨會話/崩潰恢復,用戶體驗最佳。 | 需前端開發,敏感數據 (密碼) 不宜存儲。 |
| 5. PRG 模式 (Post-Redirect-Get) | 提交後 303 跳轉結果頁,結果頁可緩存 | 避免重複提交,結果頁可安全緩存。 | 不直接解決「後退到表單頁」問題,需配合方案 1/3。 |
<?php
// 入口文件/公共配置 最早執行
// 1. 設置緩存限制器為 private (必須在 session_start 前)
session_cache_limiter('private'); // 或 'private_no_expire' (不發送 Expires)
// 2. 啟動 Session
session_start();
// 3. 雙重保險:顯式發送 Header (防 session_cache_limiter 失效)
header('Cache-Control: private, must-revalidate');
header('Pragma: private'); // 兼容 HTTP/1.0
// 業務代碼...
?>
<?php
// form.php
session_start();
$_SESSION['form_token'] = bin2hex(random_bytes(16));
?>
// submit.php
session_start();
if (empty($_POST['token']) || $_POST['token'] !== ($_SESSION['form_token'] ?? '')) {
die('非法提交或表單已過期');
}
// 處理業務...
unset($_SESSION['form_token']); // 用完即銷毀
header('Location: success.php', true, 303); // PRG 跳轉
exit();
// success.php (可安全緩存)
session_cache_limiter('public'); // 結果頁允許公共緩存
session_start();
echo "提交成功!";
?>
header() **必須在任何輸出前** (包括空白、BOM、前置 include 的 echo)。**核心矛盾**:Session 安全默認禁用緩存 vs 用戶體驗期望後退恢復。**推薦方案**:表單頁 `session_cache_limiter('private')` + `header('Cache-Control: private')`,兼顧安全(不進代理緩存)與體驗(瀏覽器私有緩存可後退恢復)。敏感操作頁堅持禁用緩存,輔以前端草稿箱方案。