session_start()必须在任何输出前调用,否则报“headers already sent”错误;常见原因包括bom、空行、提前输出;$_session无需初始化但不可存资源/闭包;销毁会话需清空数组、session_destroy()和删cookie;安全需设置httponly、secure和samesite。

PHP中session_start()必须放在输出之前
绝大多数会话失效或“Headers already sent”错误,都因为 session_start() 被调用得太晚。PHP要求Session ID通过HTTP响应头(Set-Cookie)发送,一旦有任何输出(包括空格、UTF-8 BOM、echo、HTML标签),头就已发送,再调用 session_start() 就会报错。
常见踩坑点:
- 文件开头有BOM(尤其Windows编辑器保存的.php文件)→ 用编辑器转为“UTF-8 无BOM”格式
-
session_start()前有空行、注释后多了一个换行 - 引入的配置文件(如
config.php)里提前输出了内容 - 在函数或类方法里调用
session_start(),但该函数被延迟执行(比如注册了register_shutdown_function)
$_SESSION赋值前无需初始化,但不能存资源类型
PHP自动将 $_SESSION 初始化为数组,直接赋值即可:$_SESSION['user_id'] = 123;。但以下操作会静默失败或引发警告:
- 存文件句柄(
fopen()返回的 resource)→ 序列化失败,session写入中断 - 存闭包(anonymous function)→ PHP 7.4+ 报
Fatal error: Uncaught Exception: Serialization of 'Closure' is not allowed - 存对象且未定义
__sleep()/__wakeup()→ 可能丢失状态或反序列化异常
安全建议:只存标量(string/int/bool/array)、简单对象(实现 Serializable 接口或含完整序列化逻辑)。
立即学习“PHP免费学习笔记(深入)”;
session_destroy()不等于清空$_SESSION,要分两步
调用 session_destroy() 只是删除服务端的session文件,不会清除当前请求中的 $_SESSION 数组内容,也不会删除客户端cookie。真正退出登录需三件事:
- 清空
$_SESSION数组:$_SESSION = [];或session_unset(); - 销毁服务端session数据:
session_destroy(); - 可选:删除客户端cookie(防止下次请求自动恢复旧ID):
setcookie(session_name(), '', time() - 3600, '/');
注意:如果用了自定义session处理器(如Redis),session_destroy() 是否生效取决于其实现;而 session_unset() 永远只影响当前请求的 $_SESSION 数组。
session.cookie_httponly和session.cookie_secure要按环境显式设置
默认PHP配置中,session.cookie_httponly 和 session.cookie_secure 都是 Off,意味着JS可读取session cookie(XSS风险),且HTTPS下仍可能被HTTP页面发送(降级风险)。
生产环境务必在 session_start() 前设置:
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 1);
ini_set('session.cookie_samesite', 'Strict'); // 或 'Lax'
session_start();注意:session.cookie_secure 设为1后,在HTTP协议下无法建立有效会话(cookie被浏览器拒绝);本地开发若没配HTTPS,得临时关掉它,但别提交到生产代码里。
Session生命周期细节(如过期时间、垃圾回收)高度依赖服务器配置,不要只靠 session_set_cookie_params(),更要检查 session.gc_maxlifetime 和实际存储后端(文件系统/Redis)的TTL策略。











