session.gc_maxlifetime仅控制GC判定session“可能过期”的阈值,不直接决定过期时间;实际清理依赖gc_probability/gc_divisor概率触发,且file存储与Redis存储逻辑完全不同。

session.gc_maxlifetime 不是“会话过期时间”
它只控制 PHP 垃圾回收器(GC)判定 session 文件「可能过期」的阈值,单位秒。真正删文件靠 session.gc_probability 和 session.gc_divisor 的概率触发——比如默认 1/100,即每次请求有 1% 概率跑 GC。即使设成 86400(24 小时),旧 session 文件也可能几分钟后就被清掉,也可能几天后还躺在 session.save_path 里。
它不决定用户能闲着多久不操作,也不影响客户端 cookie 是否有效;session.cookie_lifetime 才管 cookie 过期,两者必须配对设置,否则会出现「cookie 还在、服务端数据已丢」的登出现象。
改了 session.gc_maxlifetime 却没效果?检查这几点
常见失效原因不是配置写错,而是生效条件没满足:
-
ini_set('session.gc_maxlifetime', 1800)必须在session_start()之前调用,放后面就完全无效 - PHP-FPM 环境下,
.htaccess中的php_value session.gc_maxlifetime 1800不起作用(仅 mod_php 支持) - Docker 或云环境常覆盖配置,优先查
/usr/local/etc/php/conf.d/下的 .ini 文件,而不是主php.ini - 改完必须重启对应服务:
service php-fpm restart,reload不行 - 用 Redis 存 session 时,
session.gc_maxlifetime被完全忽略——得靠 Redis 自己的 TTL 设置,比如redis.session.gc_maxlifetime(若扩展支持)或手动EXPIRE
file 存储 vs Redis 存储:配置逻辑完全不同
用默认 file 存储时,session.gc_maxlifetime 有意义,但依赖 GC 概率触发,且要求 session.save_path 目录可写、磁盘未满;用 Redis 时,这个配置形同虚设。
Redis 场景下真正起效的是:
- PHP Redis 扩展是否启用了自动过期(如
redis.session.lock_expire、redis.session.gc_maxlifetime等扩展特有配置) - 你是否在
session_set_save_handler()中显式调用EXPIRE或设置 TTL - Redis 实例本身是否启用了
maxmemory+ LRU 策略,导致 session 提前被驱逐
别指望改 session.gc_maxlifetime 让 Redis 里的 session 多活一分钟。
验证当前生效值和排查快速失效
不要只看 php.ini 里写了啥,运行时用 ini_get('session.gc_maxlifetime') 打印真实值;如果用户 5 分钟后就登出,大概率不是 GC 问题:
- 检查 Nginx 或 Cloudflare 是否缓存了带
Set-Cookie的响应,导致后续请求没传 session ID - 确认浏览器没禁用 cookie,或
session.cookie_secure=1却走 HTTP - 多子域名共用 session?
session.cookie_domain必须设为.example.com(开头带点) - CLI 请求不会触发 GC,该配置在命令行下天然无效
GC 本身不可控、不精确,真要精确控制 session 生命周期,得自己接管存储,比如用数据库+定时任务,或者用 Redis 的原生 TTL 机制——别把 session.gc_maxlifetime 当成开关去拧。


















