session_start()必须在任何输出前调用,否则触发“headers already sent”错误;$_session需逐项赋值,禁用直接赋值数组;登录后须调用session_regenerate_id(true)防止会话固定;cookie参数须在session_start()前设置。

session_start() 必须在任何输出之前调用
这是最常踩的坑:只要 echo、print、HTML 标签、甚至文件开头的 BOM 字节或空格,都会导致“Headers already sent”错误。PHP 无法再发送 session cookie,后续 $_SESSION 写入失效,但不报错——你只会发现数据“丢了”。
- 检查所有包含的文件(如
config.php、header.php)是否开头有空格或 UTF-8 BOM - 避免在
session_start()前有任何echo、var_dump、print_r(哪怕只是调试用) - 如果必须调试,改用
error_log(print_r($_SESSION, true))写到日志里 - CLI 脚本中调用
session_start()是允许的,但不会自动管理 cookie,仅用于服务端状态存储
$_SESSION 是数组,但不能直接赋值整个新数组
$_SESSION 看起来像普通变量,但它背后是 PHP 的会话处理器在维护。直接 $_SESSION = ['user_id' => 123] 会切断与当前 session ID 的关联,下次请求读不到,且可能触发 session 文件被清空。
- 正确做法是逐项赋值:
$_SESSION['user_id'] = 123、$_SESSION['cart'] = $items - 要清空全部数据,用
$_SESSION = []或session_unset(),而不是unset($_SESSION) - 销毁整个会话(含 session 文件和 cookie),用
session_destroy()+setcookie()清除客户端 cookie - 注意:PHP 7.4+ 中
$_SESSION不再支持对象引用赋值(如$_SESSION['obj'] = &$obj),会触发警告
session_id() 和 session_regenerate_id() 控制会话标识安全
默认情况下,PHP 生成的 session ID 在用户首次访问时就固定了。如果登录前 ID 已暴露(比如 URL 里传过、日志里记过),攻击者可能劫持未认证会话。登录成功后必须换 ID。
- 登录成功后立刻调用
session_regenerate_id(true):true表示删除旧 session 文件,防会话固定 - 手动指定 ID(如从 token 解析)需先
session_id($new_id),再session_start();但必须确保 ID 符合session.sid_bits_per_character规则,否则启动失败 - 检查当前 ID 是否有效:用
session_status() === PHP_SESSION_ACTIVE,别只靠isset($_SESSION) - 若使用 Redis 存储 session,
session_regenerate_id()仍有效,但底层是原子 rename 操作,不是删+写
session_set_cookie_params() 必须在 session_start() 前设置
很多开发者想让 session cookie 支持 HTTPS 或禁止 JS 访问,却在 session_start() 后才调用 session_set_cookie_params()——这时已晚,参数不会生效,cookie 仍按默认规则发送。
立即学习“PHP免费学习笔记(深入)”;
- 必须放在
session_start()之前,且通常应在入口脚本最开头(如index.php) - 常见配置:
session_set_cookie_params(['secure' => true, 'httponly' => true, 'samesite' => 'Strict']) -
samesite参数在 PHP 7.3+ 才原生支持,低版本需手动拼setcookie(),且注意它不兼容session_start()自动发的 cookie - 修改 cookie 参数不影响已存在的会话,只对下一次
session_start()生效;已有用户需重新登录才能获得新 cookie 属性
session 的核心其实是“时间差”和“信任边界”:服务器靠 session ID 认人,但 ID 本身无意义,全靠存储后端(文件/Redis)和传输通道(cookie/HTTPS)兜底。最容易被忽略的是——你以为 session 还在,其实它早因超时、路径不匹配或 domain 设置错误而静默失效了。查问题时,先看 session_status() 和 session_id(),再翻 session.save_path 目录或 Redis key,比瞎猜快得多。











