跨子域名共享 PHP Session 必须同时满足三条件:Cookie 域设为带前导点的统一 domain(如 '.example.com')、所有子域名使用相同的 session.save_handler 和 save_path(推荐 Redis)、session_name() 全站一致;任一缺失均导致会话无法共享。

跨子域名共享 PHP Session 是可行的,但必须同时满足 Cookie 域设置和会话存储一致性两个硬性条件;只改 session.cookie_domain 而不统一存储后端,或漏掉 session_set_cookie_params() 的第三个参数,都会导致登录态在子域名间丢失。
session_set_cookie_params() 必须显式传入 domain 参数
很多人以为调用 ini_set("session.cookie_domain", ".example.com") 就够了,其实不够——session_set_cookie_params() 如果只传 1 或 2 个参数(比如只传 0 和 '/'),PHP 会把 $domain 当作 null 处理,最终浏览器收到的 Set-Cookie 头里没有 Domain 字段,导致 Cookie 仅绑定当前完整主机名(如 login.example.com),无法被 api.example.com 读取。
- ✅ 正确写法(三参数齐全,
$domain带前导点):session_set_cookie_params(0, '/', '.example.com'); - ❌ 错误写法(缺
$domain):session_set_cookie_params(0, '/');或session_set_cookie_params(0); - ⚠️ 注意:该函数必须在
session_start()之前调用,且不能有任何输出(包括空格、BOM)
所有子域名必须使用同一 session.save_handler 和 save_path
即使 Cookie 能跨域发送,如果各子域名的 PHP 实例把 Session 数据分别写进本地文件系统(默认 files handler),那它们读到的仍是各自独立的会话数据。浏览器带着同一个 PHPSESSID 过来,服务器却查不到对应内容。
- ✅ 推荐方案:统一用 Redis 存储,配置一致:
ini_set('session.save_handler', 'redis');+ini_set('session.save_path', 'tcp://redis-server:6379'); - ✅ 替代方案:MySQL 表 + 自定义
session_set_save_handler(),但需确保所有实例连接同一库、同一表、同字符集 - ⚠️ 避免混用:不要一部分子域名用文件,一部分用 Redis;也不要不同子域名连不同 Redis 实例(除非做集群同步)
session_name() 必须保持一致
PHP 默认用 PHPSESSID 作为 Cookie 名,但如果某个子域名手动调用了 session_name("mysession"),而另一个没调,或用了不同名字,浏览器就会为它们创建两个独立 Cookie,彻底隔离会话。
立即学习“PHP免费学习笔记(深入)”;
- ✅ 所有子域名入口代码中统一加一句:
session_name("PHPSESSID");(或任意你指定的名称,但必须全站一致) - ✅ 若用框架(如 Laravel、ThinkPHP),检查其
session.php配置中的cookie和name项是否全局对齐 - ⚠️ 测试时务必清空浏览器中所有相关子域名的旧 Cookie,否则残留的旧
PHPSESSID会干扰验证
HTTPS 环境下必须补 Secure 和 HttpOnly 标志
现代浏览器(Chrome 120+、Firefox 115+)对非 Secure 标记的跨子域 Cookie 执行更严格策略,尤其当主站启用 HTTPS 后,未标记 Secure 的 Cookie 可能被直接丢弃,导致会话失效。
- ✅ 正确写法(PHP 7.3+ 数组语法):
session_set_cookie_params(['lifetime' => 0, 'path' => '/', 'domain' => '.example.com', 'secure' => true, 'httponly' => true, 'samesite' => 'Lax']); - ✅ 若用旧版 PHP,仍可用四参数函数:
session_set_cookie_params(0, '/', '.example.com', true);(第四个参数即$secure) - ⚠️
SameSite=Lax是当前最兼容的选择;设为None则必须同时设Secure,否则会被拒绝
最容易被忽略的是:Cookie 域设置正确 + Session 存储统一 + 名称一致,这三者缺一不可;哪怕 Redis 连通了、session_name() 对上了,只要 session_set_cookie_params() 漏掉 $domain,浏览器就收不到跨域 Cookie,整个链路就断在第一步。



















