子域名和主域名的Cookie默认互不共享,想要共享必须显式设置Domain属性
Cookie的共享规则取决于Domain属性设置,默认情况下,浏览器只允许Cookie绑定到设置它的那个域名,子域名和主域名之间互不相通,要想让主域名和所有子域名共享Cookie,必须在写入Cookie时显式指定Domain为主域名(例如.example.com),这样包括www.example.com、blog.example.com在内的所有子域名都能读取这个Cookie,反过来,子域名设置的Cookie如果不带Domain属性,主域名和其他子域名都无法访问它。
写代码时被跨域问题折腾过的人,多数都踩过Cookie共享的坑,这篇文章把主域名、子域名之间的Cookie共享机制讲透,包括具体配置方法、常见坑位,以及HttpOnly、Secure等属性对共享的影响。
从一次真实登录场景说起
假设你有两个站点:www.example.com 和 blog.example.com,用户在www登录,跳转到blog时发现又变成未登录状态,为什么?因为www写入的Cookie默认只属于www.example.com,浏览器访问blog.example.com时根本不会携带这个Cookie。
理解这个现象之前,先搞清楚Cookie归属权的底层规则域名匹配算法,浏览器判断是否发送某个Cookie,不只看域名是否相等,而是看请求域名是否“域匹配”Cookie的Domain属性。
h2: Cookie的Domain属性怎么影响子域名和主域名共享?
Cookie在写入时可以携带一个Domain属性,它决定了这个Cookie的作用范围,规则如下:
- 如果不设置
Domain,Cookie仅作用于当前精确域名,所有子域名都不共享。 - 如果设置
Domain为example.com,则example.com以及它所有的子域名(www、blog、api等)都能读取。 - 如果子域名设置
Domain为blog.example.com,则只有blog.example.com及其更深层子域名(如news.blog.example.com)能读取,www和主域名都不行。
行业共识是:共享Cookie的核心就一句话写入时把Domain设为根域名。
主域名和子域名共享Cookie的三种场景
| 场景 | 目标 | 设置Domain | 示例 |
|---|---|---|---|
| 主域名登录,子域名也要登录态 | 全站共享 | .example.com |
登录example.com,blog.example.com也有效 |
| 子域名登录,主域名也要登录态 | 主域名能读 | .example.com |
在api.example.com写入,example.com可读 |
| 仅子域名之间共享 | 部分子域共享 | .sub.example.com或具体子域 |
a.sub.example.com和b.sub.example.com共享 |
第一种场景最常见,实际操作时要特别注意:Domain属性必须包含至少一个点号,也就是说,如果你写Domain=example.com(没有点),大多数现代浏览器会拒绝它,或者把它忽略并按默认规则处理,规范要求Domain值必须是“可注册域名”的后缀,通常写成.example.com(前面加点)更稳妥。
如何设置Domain属性?前端后端都要会
后端设置(以Node.js/Express为例)
:
res.cookie('token', 'abc123', {
domain: '.example.com', // 注意前面的点
path: '/',
httpOnly: true,
secure: true,
sameSite: 'lax'
});
后端设置(以Python Flask为例):
resp.set_cookie('token', 'abc123', domain='.example.com', path='/', httponly=True, secure=True)
前端设置(JavaScript):
document.cookie = "token=abc123; Domain=.example.com; Path=/; Secure; SameSite=Lax";
这里的关键点是:前端JS设置Cookie,尽管指定了Domain=.example.com,浏览器仍然遵守同站策略。只要页面本身在www.example.com或blog.example.com上,就可以设置属于.example.com的Cookie,但是如果页面的域名与指定的Domain不匹配(比如在other.com页面上设置.example.com的Cookie),浏览器会直接拒绝。
子域名和主域名共享Cookie时容易踩的坑
- Path属性忘了写或写错,如果只设置
Domain但Path默认是,那没问题,但如果设置Path=/admin,其他路径的页面拿不到Cookie。 - Secure属性在HTTP环境下失效,如果Cookie设置了
Secure但当前站点是HTTP协议,浏览器不会存储这个Cookie,本地开发时尤其容易遇到。 - SameSite属性影响跨站发送。
SameSite=Lax默认允许同站(包含同一主域下的子域名)发送,但如果设成Strict,从blog.example.com跳转到www.example.com时,Cookie不会携带,表现就是“登录状态丢失”。 - 子域名覆盖问题,如果
www和blog分别写了不同Domain的Cookie,且Cookie名相同,浏览器会同时存储两个Cookie,请求哪个域名就带哪个Cookie,可能造成数据混乱。
h2: 子域名和主域名Cookie共享失败怎么排查?从浏览器开发者工具查起
很多人在生产环境发现共享不了,第一反应是改代码,结果反复试都不行,80%的失败都能在浏览器里直接看出来。
打开Chrome开发者工具的Application面板,左侧找到Cookies,点击https://example.com和https://blog.example.com分别查看,你会看到两个域名下各自存储的Cookie列表,如果www登录后,blog的Cookie列表里没有对应的token,说明写入时Domain没设对。
实际问题定位路径
- 确认写入时的Domain值,在Network面板里找到Set-Cookie响应头,查看
Domain属性实际是什么,如果只有Set-Cookie: token=xxx没有Domain,那就是默认绑定当前域名,共享失败。 - 确认请求是否携带Cookie,看
blog.example.com的请求头有没有Cookie字段,如果没有,说明浏览器认为这个Cookie不属于blog。 - 检查浏览器存储的总数,Chrome限制每个域名最多180个Cookie,如果超了会静默丢弃,阈值在较新版本的Chrome中更严格,超过限制后老Cookie被清除。
- 检查Cookie的过期时间,Session Cookie(未设置Expires或Max-Age)在浏览器关闭后消失,如果共享需求跨会话,必须设置过期时间。
使用curl验证共享逻辑
如果你不想打开浏览器,用curl也可以模拟验证Cookie行为,先向登录接口发起请求,保存Cookie:
curl -c cookies.txt -X POST https://www.example.com/login
然后携带Cookie访问子域名:
curl -b cookies.txt https://blog.example.com/profile
如果cookies.txt里的Domain字段是.example.com,那么在请求blog时curl会自动发送这个Cookie,如果Domain是www.example.com,则不会发送,这个方法能快速判断服务端是否正确配置了Domain属性。
h2: 不考虑共享的Cookie怎么设置更安全?
并非所有业务都需要主域名和子域名共享Cookie,比如api.example.com专门处理敏感接口,你希望普通子域名页面无法读取这个接口的Cookie,那就不要设置Domain,让它默认绑定api.example.com,这样可以缩小Cookie的暴露面,减少CSRF攻击风险。
业内专家指出(仅用一次):Cookie的共享范围越大,被第三方脚本窃取的风险就越高,网站遇到XSS漏洞时,恶意脚本通常只能读取当前域名下可访问的Cookie,如果把所有Cookie都设成根域名共享,那么任意一个子域名被注入脚本,所有子域名的Cookie都会泄露。
针对不同nginx子域名场景,Cookie怎么取舍?
常见的部署结构里,前端站点是www.example.com,后端API是api.example.com,如果用户登录后需要前端调用API,通常有两种方案:
- API的Cookie也不设Domain,但前端请求通过全站域名的反向代理同源,比如
www.example.com/api/反代到内网API服务器,浏览器看到的是同一个域名,Cookie自然携带。 - API的Cookie设置Domain为
.example.com,此时所有子域名的页面都能携带这个Cookie到API接口,前提是你确信所有子域名都是可信的。
推荐方案1,虽然方案2配置简单,但它让blog.example.com这种静态站点也持有API域名的Cookie,如果静态站点被上传恶意文件,风险更大,行业共识是:尽量用同源代理,不要用Domain属性扩大范围。
h2: Cookie共享和CSRF、XSS攻击的关系安全性怎么权衡?
共享Cookie时,必须处理好安全属性。HttpOnly能防止JavaScript读取Cookie,Secure保证Cookie只在HTTPS下传输,SameSite限制跨站请求携带Cookie,这三者缺一不可。
在三方Cookie被各大浏览器逐步淘汰的今天,很多站点考虑用SameSite=None; Secure来跨站携带Cookie,但请注意:SameSite=None用于跨站(比如example.com访问api.other.com),不适用于同站不同子域名,同站(site)的定义是协议、端口、可注册域名都相同,子域名不同也算同站,所以你的子域名共享场景,应该使用SameSite=Lax或Strict,不要设置None。
如何设置HttpOnly和Secure让共享Cookie更安全?
以Java Servlet为例,设置响应头:
response.addHeader("Set-Cookie", "token=abc123; Domain=.example.com; Path=/; HttpOnly; Secure; SameSite=Lax");
用Spring Boot则更简洁:
ResponseCookie cookie = ResponseCookie.from("token", "abc123")
.domain(".example.com")
.path("/")
.httpOnly(true)
.secure(true)
.sameSite("Lax")
.build();
上述代码同时设置了
HttpOnly与Secure。HttpOnly确保document.cookie无法读出该Cookie,即使子域名页面被注入XSS脚本,攻击者也只能通过伪造请求的方式利用,无法直接窃取值。
h2: 本地开发调试时,localhost和子域名怎么共享Cookie?
本地开发经常用localhost模拟主域名,用myapp.test模拟子域名,这里有两个常见问题:
Domain=localhost合法吗?不合法,大多数浏览器要求Domain属性必须包含至少一个点号,localhost只有一个标签,会被拒绝,所以如果前端跑在localhost:3000,后端跑在localhost:8080,它们之间Cookie共享靠的是同Host不同端口,端口不影响Cookie域名,所以只要把Cookie写到localhost(不设置Domain即可),两个端口能共享。- 如果非要模拟子域名场景,推荐用
nip.io或其他通配域名解析服务,把example.com和blog.example.com都指向0.0.1,在/etc/hosts里手动加也行:
0.0.1 example.com
127.0.0.1 blog.example.com
然后在代码里把Cookie的Domain设为.example.com,浏览器就会把它们当作同站处理,注意:.example.com这种正式后缀在国内需要备案,本地测试没问题,生产环境请使用你实际注册的域名。
一个典型问题:用户在www子域名登录了,主域名example.com却拿不到Cookie
很多网站在做整站迁移时,把原页面从www.example.com跳转到example.com,结果登录状态全丢,排查过程如下:
- 检查重定向响应头里是否有
Set-Cookie,如果重定向发生在服务端,且原服务在响应中设置了Domain=.example.com,浏览器会存储,然后跟随重定向访问example.com时携带Cookie,登录态应该保持。 - 如果重定向用JS在浏览器端跳转,且页面未加载完就执行
window.location,可能把未设置的Cookie丢掉。 - 如果登录接口返回的Cookie没有Domain,只有
www.example.com,那跳转后自然丢失。修复方式:在登录接口和所有写Cookie的后端代码中统一加上.example.com。
h2: Q&A:关于cookies设置域名时,子域名和主域名怎么共享的常见问题
问题1:子域名能否读取主域名设置的Cookie?
不能,除非主域名写入Cookie时把Domain设为.example.com,如果主域名不设置Domain,Cookie只属于example.com本身,所有子域名(包括www)都无法读取。
问题2:在子域名写入Cookie,主域名能读到吗?
同样不能,除非子域名写入时把Domain设为根域名.example.com,例如在blog.example.com页面执行document.cookie="token=x; Domain=.example.com; Path=/;",然后访问example.com就能读到这个Cookie。
问题3:如果两个子域名设置了相同名称但不同Domain的Cookie,会发生什么?
浏览器会把它当成两个独立的Cookie存储,请求www.example.com时,带上Domain=www.example.com的那个;请求blog.example.com时,带上Domain=blog.example.com的那个,如果这两个Cookie都指定了.example.com,浏览器只保留一个,后写入的覆盖先写入的。共享Cookie设计时统一用一个Cookie名,并固定Domain为根域名,就能避免冲突。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/621593.html





