本文目录导读:

- 📚 目录导读
- 为什么子域名Cookie让人头疼?
- 核心参数:
domain属性的正确打开方式 - 实战代码:三步设置跨子域共享Cookie
- 安全雷区:HttpOnly、Secure 与 SameSite 的“黄金搭配”
- 登录态互通与注销:一台服务器管理多个子站
- 常见故障排查:为什么我设置了却无效?
- 高频问答:开发者最纠结的5个问题
PHP 跨子域名Cookie设置全攻略:从入门到生产环境踩坑指南**
📚 目录导读
- 为什么子域名Cookie让人头疼?—— 同源策略与Cookie作用域
- 核心参数:
domain属性的正确打开方式 - 实战代码:三步设置跨子域共享Cookie
- 安全雷区:HttpOnly、Secure 与 SameSite 的“黄金搭配”
- 登录态互通与注销:一台服务器管理多个子站
- 常见故障排查:为什么我设置了却无效?
- 高频问答:开发者最纠结的5个问题
为什么子域名Cookie让人头疼?
当你拥有 a.example.com 和 b.example.com 两个站点,默认情况下浏览器将两者视为完全隔离的源(Origin),PHP 的 setcookie() 函数如果不指定 domain 参数,Cookie 默认绑定在当前完整主机名上(如 a.example.com),导致 b.example.com 无法读取,这并非 PHP 的缺陷,而是浏览器出于隐私保护实施的同源策略(Same-Origin Policy)。
核心逻辑:Cookie 的路径(Path)控制目录范围,而 Domain 属性控制主机名作用域,要实现子域共享,必须将 Domain 设为 .example.com(注意前导点,代表允许所有子域及根域)。
核心参数:domain 属性的正确打开方式
在 PHP 中,setcookie 函数的签名如下:
setcookie(string $name, string $value = "", array $options = [])
关键选项(PHP 7.3+ 推荐使用数组形式):
domain:必须设置为.example.com(带点前缀),否则无效。secure:仅通过 HTTPS 传输。httponly:禁止 JavaScript 访问(防 XSS)。samesite:CSRF 防护,建议设为Lax或Strict。
注意:如果直接在 a.example.com 下设置 domain = example.com(不带点),部分浏览器(如火狐)会拒绝接受,而 Chrome 则可能自动忽略该参数。规范要求必须带点。
实战代码:三步设置跨子域共享Cookie
场景:用户在 login.example.com 登录,需要在 shop.example.com 和 blog.example.com 共享会话。
第一步(登录处理):
// 在 login.example.com 的登录脚本中
setcookie(
'auth_token',
'加密后的用户标识',
[
'expires' => time() + 86400 * 7, // 7天有效
'path' => '/',
'domain' => '.example.com', // 关键:带点
'secure' => true, // 若全站HTTPS
'httponly' => true,
'samesite' => 'Lax'
]
);
第二步(跨子域读取):
在任何子域(如 shop.example.com)的 PHP 脚本中,通过 $_COOKIE['auth_token'] 直接读取,无需额外配置。
第三步(安全删除):
setcookie('auth_token', '', time() - 3600, '/', '.example.com');
安全雷区:HttpOnly、Secure 与 SameSite 的“黄金搭配”
- 绝对要加
HttpOnly:防止黑客通过 XSS 窃取 Cookie,若子域中存在未修复的脚本漏洞,攻击面会扩散至所有共享该 Cookie 的站点。 Secure必须开启:若你启用了 HTTP 与 HTTPS 混合访问,浏览器可能通过 HTTP 明文发送 Cookie,被中间人截获。SameSite=Lax:如果你有跨站跳转需求(如从a.example.com通过链接跳转到b.example.com并携带 Cookie),Strict会阻断这种场景。Lax保留 GET 请求携带,安全性够用。- 子域信任边界:共享 Cookie 意味着任何一个子域被入侵,其他子域安全一同沦陷,如果子域间信任级别不同(如
dev.example.com与生产环境),切勿共享域。
登录态互通与注销:一台服务器管理多个子站
推荐架构:使用 中央认证服务器(CAS),所有子域重定向到 login.example.com,验证通过后设置上述 Cookie,其他子域仅需解密 Cookie 信息,无需再次拉取用户表。
注销逻辑: 必须同时销毁所有子域的 Cookie,最稳妥方案是调用统一注销接口:
// 在所有子域的注销脚本中执行
setcookie('auth_token', '', time() - 3600, '/', '.example.com');
destroySession(); // 同时清理session文件
常见故障排查:为什么我设置了却无效?
| 症状 | 原因排查 |
|---|---|
| Chrome 能设置,Safari 失效 | Domain 未加前导点 ,或本地测试使用了 localhost(应配置虚拟主机为 sub.example.com) |
| 设置成功但跨子域读不到 | 检查 path 是否设为 (默认当前目录) |
| Cookie 被浏览器拒绝 | 服务器时间偏差过大,或 expires 格式错误(应使用 Unix 时间戳) |
| HTTPS 页面无法获取 | secure 参数为 true,但页面通过 HTTP 访问 |
| 子域间无法同步 | 检查 cloudflare/Nginx 是否写了 Set-Cookie 头的 domain 覆盖逻辑 |
高频问答:开发者最纠结的5个问题
Q1:domain 不加点,为什么 Chrome 也能工作?
Chrome 对 domain=example.com 和 domain=.example.com 的解析不严格区分,但 Firefox 严格遵循 RFC 6265,要求必须带点,为确保通用性,务必加前导点。
Q2:如果子域名是 shop.example.com.cn,怎么设置?
应设为 .example.com.cn,即注册根域(如 com.cn)的下一级。不要设为 .com.cn,否则所有以 com.cn 结尾的站点都能读取(严重安全漏洞)。
Q3:能否用 JS 设置跨子域 Cookie?
可以,但会暴露给 XSS,前端设置时需指定 domain=.example.com,且必须用 document.cookie 拼接字符串。强烈建议后端设置,否则 setcookie 的 HttpOnly 功能失效。
Q4:设置了永久的 auth_token,但用户换台电脑就无法登录?
跨子域 Cookie 与设备绑定无关,问题通常出在 Token 存储不在服务端,应在 Redis/MySQL 存储该 Token 的会话标识,而非将明文用户信息直接放进 Cookie。
Q5:子域使用不同端口(如 a.example.com:8080),Cookie 能共享吗?
端口不参与 Cookie 作用域判断,只要主机名一致(a.example.com 与 a.example.com:8080),共享正常,但 a.example.com 与 b.example.com 则必须按上文配置。
通过上述配置与排查,你的 PHP 应用即可在多个子域间无缝共享用户登录状态,最后再强调一次:域名的前导点、Secure 与 SameSite 这三点是生产环境的生死线,请务必在测试环境用 Safari 与 Firefox 交叉验证。