你有没有遇到过:用户刚登录,刷新页面就提示“请重新登录”;或者加到购物车的商品,点个链接就没了?这八成是 PHP 的会话(Session)没管好。
Session 不是自动靠谱的
PHP 默认开启 session_start() 就能用 $_SESSION,但光靠它远远不够。服务器重启、临时目录被清空、session.save_path 权限不对、甚至浏览器禁用了 Cookie,都会让会话“断联”。更常见的是:本地测试好好的,一上服务器就失效——大概率是 session.save_path 指向了 /tmp 且没写入权限,或者 PHP-FPM 和 Apache 用户不一致导致文件写不进去。
动手改几个关键配置
打开 php.ini,检查这几项:
session.save_handler = files
session.save_path = "/var/www/sessions"
session.cookie_httponly = 1
session.cookie_secure = 1 ; HTTPS 环境下务必开启
session.gc_maxlifetime = 1440 ; 默认 24 分钟,太短,建议调到 3600(1 小时)别忘了手动创建 /var/www/sessions 目录,并给 Web 服务用户(比如 www-data 或 nginx)赋写权限:chown www-data:www-data /var/www/sessions && chmod 700 /var/www/sessions。
登录后记得重生成 ID
用户登录成功那一刻,必须执行 session_regenerate_id(true),防止会话固定攻击(Session Fixation)。老 ID 删掉,新 ID 马上生效:
<?php
session_start();
// ... 验证用户名密码通过
session_regenerate_id(true); // 删除旧 session 文件,启用新 ID
$_SESSION['user_id'] = 123;
$_SESSION['logged_in'] = true;
?>跨子域共享会话?加 domain 就行
比如你的站点有 www.example.com 和 api.example.com,登录后想两边都能读到 $_SESSION,初始化时加一句:
<?php
session_set_cookie_params([
'lifetime' => 3600,
'path' => '/',
'domain' => '.example.com', // 注意开头的点
'secure' => true,
'httponly' => true,
'samesite' => 'Lax'
]);
session_start();
?>不想依赖文件?试试 Redis
高并发场景下,文件型 Session 容易 IO 瓶颈。装个 Redis,三步切换:
1. 安装 redis 扩展:sudo apt install php-redis
2. 修改 php.ini:
session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379?auth=yourpass"3. 重启 PHP 服务。不用改一行业务代码,$_SESSION 照样工作,而且速度更快、支持分布式。
会话管理不是写完 session_start() 就完事的小功能,它是登录态、权限控制、临时数据流转的底层支柱。多看一眼 phpinfo() 里的 Session 区块,少踩一半坑。