Session 只在當前請求有效,下一次請求變空陣列,是典型的 Session 持久化失效。本文記錄一次真實排查過程,涵蓋權限、配置、程式碼三大維度,並給出通用排查清單。
# 檢查 php.ini
session.save_path = "/var/lib/php/session"
# 進入目錄檢查權限
ls -ld /var/lib/php/session
# 輸出:drwxrwx--- 2 root apache 4096 ... <-- 其他用戶無讀寫權限!
Web 進程使用者(如 www-data/apache/nginx)無法寫入 Session 檔案,導致每次請求都建立新 Session 但無法持久化。
# 方案 1:修改目錄擁有者(推薦)
chown -R www-data:www-data /var/lib/php/session
chmod 770 /var/lib/php/session
# 方案 2:修改權限(不推薦生產環境)
chmod 777 /var/lib/php/session
# 方案 3:自定義儲存路徑至專案可寫目錄
# php.ini 或程式碼中:
ini_set('session.save_path', __DIR__ . '/../storage/sessions');
# 確保該目錄存在且 Web 使用者可寫
| 順序 | 檢查項目 | 指令/代碼 |
|---|---|---|
| 1 | session.save_path 路徑是否存在 | php -i | grep session.save_path |
| 2 | 該目錄 Web 使用者是否可讀寫 | ls -ld /path/to/session; sudo -u www-data touch /path/to/session/test |
| 3 | 磁碟空間是否滿 | df -h /path/to/session |
| 4 | inode 是否耗盡 | df -i /path/to/session |
| 5 | session.gc_probability/divisor 導致過度 GC | php -i | grep 'session.gc' |
| 6 | 多專案共用 save_path 造成衝突 | 各專案設定獨立 save_path |
| 7 | 程式碼中是否有 session_destroy()/unset($_SESSION) | grep -r "session_destroy" . |
| 8 | session.name 是否統一(跨子域) | ini_set('session.name', 'MYAPP') |
| 9 | Cookie 域/路徑/安全標誌設定 | session_set_cookie_params() |
| 10 | 使用 Redis/Memcached 存儲 Session | php.ini: session.save_handler=redis |
# 安裝 php-redis 擴展
# php.ini
session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379?auth=yourpass&database=1"
# 或多節點高可用
session.save_path = "tcp://redis1:6379?weight=1, tcp://redis2:6379?weight=1"
優點:跨伺服器共享、效能高、支援集群、可設定 TTL、避免檔案系統權限/Inode 問題。
核心教訓:Session 失效 90% 是儲存層權限或路徑問題。遇到「只在當前請求有效」,第一時間檢查 session.save_path 目錄權限。