必须按 session.lifetime×60 秒校准文件修改时间判断过期,因 lifetime 单位为分钟而 mtime 为秒;cache:clear 不影响 session 文件,因其由 file 驱动独立管理。

直接删 storage/framework/sessions/ 下所有文件会误删活跃会话,必须按生命周期判断——Laravel 的 session.lifetime 是分钟单位,而文件修改时间(mtime)是秒级,换算时漏掉乘 60 就会导致全部误删或一个不删。
为什么不能用 cache:clear 清 Session
cache:clear 只清 Cache::put() 写入的应用缓存,和 session 完全无关。Session 文件由 file 驱动独立管理,路径固定在 storage/framework/sessions/,底层不走 Cache 门面,也不响应任何缓存事件。执行完 cache:clear 后用户仍能正常登录,就是最直接的验证。
手动遍历文件时必须校准时间单位
常见错误是把 config('session.lifetime') 当成秒来用。它默认值是 120(分钟),但 $file->getMTime() 返回的是 Unix 时间戳(秒),所以过期判断必须写成:time() - $file->getMTime() > config('session.lifetime') * 60。
容易踩的坑包括:
- 用
filemtime()替代$file->getMTime(),但没做异常捕获,遇到权限问题会中断整个循环 - 忽略
session.cookie_lifetime和session.lifetime的差异:前者控制 Cookie 过期,后者才决定服务端 session 文件是否该被回收 - 没加
is_file($file->getRealPath())判断,导致符号链接或临时文件被误判
推荐封装为可调度的 Artisan 命令
比起写死在 Kernel.php 的闭包,更稳妥的做法是新建命令类:php artisan make:command CleanupExpiredSessions,然后在 handle() 中实现清理逻辑。这样支持单独测试、加日志、设超时,也方便后续扩展(比如统计清理数量、发告警)。
关键点有:
- 使用
Filesystem门面替代原生File辅助函数,避免未引入use Illuminate\Support\Facades\File;导致报错 - 路径硬编码为
storage_path('framework/sessions'),不要拼接字符串,防止 Windows 路径分隔符出错 - 加
if ($this->confirm('确认清理过期 session?')) { ... }防止误触发,尤其在线上环境 - 调度时用
$schedule->command('cleanup:expired-sessions')->dailyAt('04:00'),避开业务高峰
session 文件名不含时间戳,无法按名过滤
Laravel 的 session 文件名是随机哈希(如 abc123...xyz),不包含创建或过期时间。这意味着你不能靠文件名排序或正则匹配来筛选,唯一可靠依据只有 mtime。有些团队尝试用 touch 更新文件时间来“续命”,但这是危险操作——它会让本该失效的 session 继续有效,破坏认证安全性。
真正需要关注的其实是配置一致性:session.driver=file 且 session.lifetime 在 config/session.php 和实际部署环境里完全一致。线上若用了 Redis 驱动,这个命令就完全无效——它只对 file 驱动有意义。











