在 Django 项目里,会话 Cookie 的域属性直接决定了浏览器在哪些主机下会回传该 Cookie。当多个业务子系统以不同子域部署时,若会话域配置不当,就会出现跨子域共享同一会话的问题,带来数据越权与状态污染的隐患。理解并精确控制这一配置,是构建安全多租户或模块化站点时的基础工作。

一、Django 会话域的核心配置项
Django 通过 settings 中的 SESSION_COOKIE_DOMAIN 来控制会话 Cookie 的 Domain 属性。当该值为 None(默认值)时,Django 会使用当前请求的主机头作为 Cookie 域,且不会带前导点,这意味着 Cookie 仅对当前完全限定的主机名有效,天然避免了跨子域共享。
如果将其设置为 .ippipp.com 这种带点的形式,浏览器就会在 ippipp.com 及其所有子域下发送该 Cookie。这种配置常用于需要单点登录的统一鉴权体系,但一旦用错场景,比如本来希望 a.ipipp.com 和 b.ipipp.com 完全独立,却误设成 .ipipp.com,两个子域的后端就会读到同一个 sessionid,引发用户态混淆。因此精确控制的第一步,是明确业务是否需要子域互通。
1.1 默认行为下的隔离效果
在未显式配置 SESSION_COOKIE_DOMAIN 时,假设用户访问 https://shop.ipipp.com,Django 下发的 Set-Cookie 头类似如下内容:
Set-Cookie: sessionid=abc123; Domain=shop.ipipp.com; Path=/; HttpOnly; Secure
此时浏览器只会在请求 shop.ipipp.com 时携带 sessionid,访问 https://blog.ipipp.com 则不会发送,实现了子域间隔离。这种方式最适合多业务线独立运营、互不信任的场景。
1.2 显式精确绑定子域
若希望通过配置明确而非依赖默认值,可直接将域绑定到具体子域:
# settings.py # 仅允许 shop.ipipp.com 使用该会话 Cookie SESSION_COOKIE_DOMAIN = 'shop.ipipp.com'
该写法与默认行为等效但更直观,也方便在多个 settings 模块间做环境区分。需要注意的是,Domain 值不应带前导点,否则又会退化为泛子域匹配。
二、中间件与请求主机头的处理
Django 的会话中间件 django.contrib.sessions.middleware.SessionMiddleware 在写入 Cookie 时依赖 request.get_host() 获取主机名。若项目前面有 Nginx 等反向代理,且未正确传递 Host 头,可能导致 Django 取到错误域名,间接影响 Cookie 域判定。
在 Nginx 配置中,应确保存在 proxy_set_header Host $host; 或传递具体域名,同时在 Django 的 ALLOWED_HOSTS 中登记对应子域,避免主机头攻击并维持 Cookie 域稳定。
2.1 反向代理下的配置示例
下面是一个典型的 Nginx 子域代理片段,保证后端拿到准确主机头:
server {
listen 80;
server_name shop.ipipp.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
对应 Django 侧需声明允许的主机:
# settings.py ALLOWED_HOSTS = ['shop.ipipp.com', 'blog.ipipp.com']
这样每个子域独立走各自 server 块,Django 基于准确 Host 生成 Cookie 域,不会彼此串扰。若用同一个 server 块通配所有子域再内部路由,就要额外在应用层根据域名动态设置 SESSION_COOKIE_DOMAIN,复杂度明显上升。
三、动态控制会话域的高级实践
当同一套 Django 代码部署给多个子域、且各子域必须完全隔离时,可在中间件中根据请求主机动态覆写 Cookie 域,从而绕开全局 settings 的单一限制。
3.1 自定义中间件实现
下面示例中间件在响应前将会话 Cookie 域设为当前请求的精确主机,确保无跨子域泄漏:
# middleware.py
from django.utils.deprecation import MiddlewareMixin
class StrictSubdomainSessionMiddleware(MiddlewareMixin):
def process_response(self, request, response):
host = request.get_host().split(':')[0]
if 'sessionid' in request.COOKIES:
response.set_cookie(
'sessionid',
request.COOKIES['sessionid'],
domain=host,
httponly=True,
secure=request.is_secure()
)
return response
将该中间件置于 SessionMiddleware 之后注册,即可在不改动全局 SESSION_COOKIE_DOMAIN 的情况下,按子域精确下发 Cookie。其优点是与默认隔离语义一致,且支持灵活部署;缺点是需自行维护 Cookie 属性,容易遗漏 Secure 或 SameSite 等安全标志。
3.2 与 SameSite 策略配合
现代浏览器支持 SameSite 属性,将其设为 Strict 或 Lax 可进一步限制跨站请求携带 Cookie。在 settings 中配置:
SESSION_COOKIE_SAMESITE = 'Lax' SESSION_COOKIE_SECURE = True
即便域配置出现微小偏差,SameSite 也能提供一层防御,减少跨子域脚本误带会话的风险。但注意 SameSite 不替代域隔离,二者应叠加使用。
四、常见误区与排查清单
不少开发者在调试跨子域问题时,只检查了浏览器 Application 面板里的 Cookie 列表,却忽略了响应头中 Set-Cookie 的 Domain 字段。若看到 Domain 为 .ipipp.com,基本可断定是 SESSION_COOKIE_DOMAIN 被设成了泛域名。
另一个误区是认为清空浏览器 Cookie 就能解决串号,实际上若服务端配置不变,重新登录后依旧会下发宽域 Cookie。正确的排查顺序是:确认 settings 值、抓包看响应头、核对 Nginx Host 传递、检查是否有自定义中间件覆写。按此清单处理,大多隔离故障都能快速定位。
| 配置方式 | 跨子域共享 | 适用场景 |
|---|---|---|
| SESSION_COOKIE_DOMAIN = None | 否 | 子域完全独立 |
| SESSION_COOKIE_DOMAIN = 'shop.ipipp.com' | 否 | 明确单子域 |
| SESSION_COOKIE_DOMAIN = '.ipipp.com' | 是 | 单点登录 |
通过上述配置组合与架构约束,Django 项目可以在多子域环境下精准掌控会话边界,既避免不必要的 Cookie 共享,也为后续可能的统一鉴权预留清晰改造路径。
Djangosession_cookie_domainCookie隔离修改时间:2026-08-06 04:00:33