ThinkPHP多域名共享session需显式配置session.cookie_domain为'.example.com',否则浏览器cookie按域名隔离;token方案须将域名纳入缓存key;CORS需指定Origin且允许credentials;所有子域必须共用同一session.name。
ThinkPHP 的 session 域名绑定必须显式设置
默认情况下,ThinkPHP 不会自动适配多域名共享 session,哪怕你用了同一个 Redis 或数据库存 session,浏览器端的 cookie 仍按当前域名隔离。关键在 PHP 底层的 cookie 域名策略,不是框架能绕过的。
实操上,得在
里手动配置:
常见错误现象:
登录后,在
拿不到
,
为空 —— 八成是漏了这个配置,或写成了
(没点)导致子域不生效。
注意点:
立即学习
“
PHP免费学习笔记(深入)
”;
必须以点开头(如
),否则只匹配精确域名
不能跨主域(比如
和
无法共享)
本地开发时用
无法设 domain,得切到真实二级域名(如
)测试
Token 认证场景下,
存 token 要带 domain 前缀
如果不用 session,而是用 token(如 JWT 或自定义字符串)+ 缓存做登录态,那“多域名”意味着不同域名请求要能查到同一份 token 数据。但直接存
会导致冲突 —— 同一用户在
和
登录,后一次会覆盖前一次。
解决方案是把域名信息作为 key 的一部分:
但更稳妥的做法是:统一用主域标识,比如从
提取根域:
用
粗略处理
或借助
+ 白名单判断(如只允许
及其子域)
避免前端伪造 host 头,服务端要做
校验
跨域请求时,
配置和
必须同时生效
前端发请求(比如 Vue 项目在
)调用
接口登录,若没正确配 CORS,浏览器会直接拦截响应,连 session cookie 都不会带过去。
ThinkPHP 6+ 的中间件里要确保:
响应头包含
(不能是
,否则
失效)
包含
预检请求(OPTIONS)也要返回这些头,且不能缓存(加
更稳)
容易踩的坑:
写成
后,前端 JS 里还写了
,结果 fetch 拿不到 cookie —— 浏览器直接静默拒绝,控制台可能只报 “CORS error”,不提示具体原因。
Redis 存 session 时,
不能各域名用不同值
有人为区分域名,给每个子域单独配一个
(比如
、
),这反而破坏单点登录:登录态被拆成多个独立 session,无法互通。
正确做法是所有域名共用一套 session 配置,包括:
统一(如默认
)
一致(都用 redis)
和
指向同一个实例
性能影响很小,但一旦 name 不一致,框架底层就认为是两个 session 实例,
启动的是完全隔离的存储空间 —— 这个细节很多人在调试时忽略,反复清 cookie 也没用。
复杂点在于:上线后不同团队维护不同子域,各自改配置却没同步,结果生产环境部分域名登不上。这种问题往往要翻日志比对
输出才看得出来。
session.cookie_domainconfig/session.php
return [
'type' => 'redis',
'cookie_domain' => '.example.com', // 注意开头的点,表示匹配所有子域
];
login.example.comadmin.example.comsession_id$_SESSIONexample.comcookie_domain.example.com.example.com.other.comlocalhostdev.example.comthink\facade\Cache$cache->set('token_'.$uid, ...)a.example.comb.example.com
$domain = request()->host();
$cacheKey = 'token_' . $uid . '_' . md5($domain);
$cache->set($cacheKey, $tokenData, 3600);
request()->host()str_replace('www.', '', $host)parse_url()example.comin_array($domain, ['example.com', 'a.example.com', 'b.example.com'])CORSwithCredentialsfe.example.comapi.example.comAccess-Control-Allow-Origin: https://fe.example.com*withCredentialsAccess-Control-Allow-Credentials: trueAccess-Control-Max-Age: 0Access-Control-Allow-Origin*credentials: 'include'session.namesession.namePHPSESSID_APHPSESSID_Bsession.namePHPSESSIDsession.save_handlersession.redis.hostportsession_start()session_id()