許多開發者只知道改 `session.gc_maxlifetime`,卻不了解 PHP Session 採用**概率性垃圾回收 (GC)** 機制:過期文件不會立即刪除,而是等待下一次 GC 執行時才清理。本文徹底講透 Session 存儲、客戶端識別、過期判定、GC 觸發條件、特殊場景處理,給出生產環境配置建議。
| 方式 | 機制 | 優缺點 |
| Cookie (默認) | 瀏覽器自動攜帶 `PHPSESSID=xxx` | 透明、安全,需啟用 Cookie |
| URL 參數 | `?PHPSESSID=xxx` | 禁用 Cookie 時備用,但易泄露(收藏夾、Referer、日志) |
配置:`session.use_cookies=1`、`session.use_only_cookies=1` (建議,防 URL 泄露)、`session.use_trans_sid=0` (禁用 URL 傳遞)。
過期判定邏輯:文件 mtime + gc_maxlifetime
- 判斷依據:文件最後修改時間 (mtime) 距今是否超過 `session.gc_maxlifetime` (默認 1440 秒 = 24 分鐘)。
- 關鍵:`$_SESSION` 數據**未修改**不會更新文件 mtime。純讀取頁面不會延長壽命。
- 過期文件**不會自動消失**,需等待 GC 掃描刪除。
GC 觸發機制:概率性 (請求級)
; php.ini 默認
session.gc_probability = 1
session.gc_divisor = 100
; 每次請求有 1/100 = 1% 概率觸發 GC
; 高併發建議:1/1000 ~ 1/5000
session.gc_divisor = 5000
- GC 觸發時機:`session_start()` 內部,在 `open`、`read` 後執行。
- GC 掃描 `session.save_path` 下所有 `sess_*` 文件,刪除 mtime 超過 `gc_maxlifetime` 的文件。
- 多應用共享 save_path 危險:若不同應用 `gc_maxlifetime` 不同,**最小值生效**,導致長壽命應用 Session 被誤刪。
特殊場景:活躍用戶但 Session 數據不變 → 被誤刪
用戶持續操作但只讀不寫 Session,文件 mtime 不更新,超過 `gc_maxlifetime` 後下次 GC 被刪除。
解決:定期更新 Session 觸發 mtime 刷新
<?php
// 每 60 秒寫入一次時間戳,保持文件 mtime 更新
if (!isset($_SESSION['_last_active']) || time() - $_SESSION['_last_active'] > 60) {
$_SESSION['_last_active'] = time(); // 觸發 write,更新 mtime
}
?>
生產環境配置建議 (php.ini / .user.ini / ini_set)
| 參數 | 建議值 | 說明 |
| session.save_handler | files / redis / memcached | 集群環境必須用共享存儲 (Redis 推薦) |
| session.save_path | /var/lib/php/sessions (獨立目錄, 700 權限) | 不可用 /tmp (共享、不安全、定期清理) |
| session.gc_maxlifetime | 86400 (1天) 或業務需求 | 配合下方 _last_active 邏輯 |
| session.gc_probability | 1 | |
| session.gc_divisor | 1000 ~ 5000 | 降低 GC 頻率,減少 I/O 衝擊 |
| session.use_cookies | 1 | |
| session.use_only_cookies | 1 | 禁用 URL 傳 SID,提升安全 |
| session.cookie_httponly | 1 | 防 XSS 竊取 Session ID |
| session.cookie_secure | 1 (僅 HTTPS) | HTTPS 站點必開 |
| session.cookie_samesite | Lax / Strict | PHP 7.3+ 防 CSRF |
| session.hash_function | 1 (SHA-1) | 比 MD5 更安全 |
| session.hash_bits_per_character | 5 | 縮短 SID 長度 |
集群/分布式環境:棄用文件,改用 Redis
; php.ini (需安裝 phpredis 擴展)
session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379?auth=yourpass&database=0"
; 內建支持鎖、原子操作、TTL 自動過期,無需手寫 GC
或使用成熟庫:predis/predis + 自定義 `SessionHandlerInterface` 實現。
總結:Session 過期「三步走」
- 設置壽命:`session.gc_maxlifetime` 定義「不活躍最大秒數」。
- 保持活躍:關鍵頁面寫入 `$_SESSION['_active'] = time()` 刷新 mtime。
- 控制清理:調整 `gc_probability/divisor` 避免高峰期 GC 卡頓;共享存儲環境隔離 `save_path` 或遷移 Redis。
- 上一篇文章: PHP 檢測並移除文件 BOM 頭完整代碼
- 下一篇文章: Web 文件上傳漏洞系列(一):文件名檢測繞過導致任意文件寫入
Copyright ©
程式碼之家 All Rights Reserved