
django 前后端分离场景下 csrf 验证失败,常因未同时传递 cookie 和 `x-csrftoken` 请求头所致;本文详解“双重提交 cookie”机制原理、配置要点及 nginx 代理下的关键修复步骤。
在 Django 前后端分离架构中(如前端运行于 http://dev.admin.lt,后端 API 部署于 https://api.admin.lt),CSRF 验证持续失败的根本原因往往并非配置遗漏,而是对 Django CSRF 防护机制的理解偏差——Django 默认采用 Double Submit Cookie(双重提交 Cookie) 模式,它要求客户端必须同时满足两个条件:
- ✅ 在请求 Cookie 中携带 csrftoken(由 Django 自动设置,HttpOnly 安全属性不影响读取);
- ✅ 在请求头中显式传递 X-CSRFToken 字段,其值必须与 Cookie 中的 csrftoken 完全一致。
仅传 Cookie 或仅传 Header 均会触发 403 Forbidden: CSRF verification failed 错误——这正是你观察到 request.headers 中缺失 X-CSRFToken 的原因:Django 不会自动从 Cookie 提取并注入该 Header,必须由前端主动设置。
✅ 正确的前端请求示例(JavaScript / Axios)
// 1. 先获取 csrftoken(通常从登录响应或独立 endpoint 获取)
const getCookie = (name) => {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
return parts.length === 2 ? parts.pop().split(';').shift() : null;
};
// 2. 发起 POST 请求时,同时设置 Cookie(浏览器自动携带)和 X-CSRFToken Header
axios.post('https://api.admin.lt/auth/login/', { username, password }, {
headers: {
'X-CSRFToken': getCookie('csrftoken'), // ⚠️ 必须显式设置!
'Content-Type': 'application/json',
},
withCredentials: true // ⚠️ 关键:启用凭据(含 Cookie)跨域发送
});? 关键配置核查清单(Django + Nginx)
| 项目 | 正确配置 | 说明 |
|---|---|---|
| CSRF_COOKIE_SECURE | True | 因使用 HTTPS,必须开启,否则 Cookie 不会被浏览器发送 |
| CSRF_COOKIE_HTTPONLY | False(⚠️ 重点修正) | True 会阻止 JavaScript 读取 Cookie → 无法获取 token 填入 X-CSRFToken header!前后端分离场景下必须设为 False |
| CSRF_COOKIE_SAMESITE | 'Lax'(推荐)或 'None'(若需跨站) | 若设为 None,必须同时启用 CSRF_COOKIE_SECURE=True;'Lax' 在多数场景更安全且兼容性好 |
| CSRF_COOKIE_DOMAIN | '.admin.lt'(带前导点) | 确保 Cookie 能被 dev.admin.lt、api.admin.lt 等子域共享 |
| Nginx proxy_set_header |
必须添加: proxy_set_header X-CSRFToken $http_x_csrf_token; |
否则 Nginx 会剥离自定义 Header!你的当前配置缺失此行,导致 X-CSRFToken 根本未到达 Django |
? Nginx 修复配置(追加至 location / 块内):location / { proxy_pass http://localhost:8000; proxy_set_header X-CSRFToken $http_x_csrf_token; # ← 关键!透传 CSRF Header proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header Host $host; # ... 其他已有配置保持不变 }
⚠️ 其他易错点排查
- CORS_ALLOW_CREDENTIALS = True:已正确配置,确保前端 withCredentials: true 生效;
- CSRF_TRUSTED_ORIGINS:必须包含前端完整协议+域名(如 "http://dev.admin.lt" 和 "https://dev.admin.lt" 需同时列出);
- ALLOWED_HOSTS:应包含 api.admin.lt 和可能的负载均衡 IP(如 ['api.admin.lt', '127.0.0.1']);
- 中间件顺序:确保 'django.middleware.csrf.CsrfViewMiddleware' 在 'django.contrib.sessions.middleware.SessionMiddleware' 之后、且未被 CORS 中间件错误拦截。
✅ 验证是否生效
- 使用浏览器开发者工具 → Network → 查看请求的 Request Headers:确认同时存在 Cookie: csrftoken=xxx 和 X-CSRFToken: xxx;
- 在 Django 视图中临时打印:
def login_view(request): print("CSRF Token in header:", request.META.get('HTTP_X_CSRFTOKEN')) print("CSRF Token in cookie:", request.COOKIES.get('csrftoken')) # ... 业务逻辑二者值应完全一致。
总结:Django CSRF 的“双重提交”不是可选优化,而是强制安全模型。解决核心在于——前端严格同步传递 Cookie 与 Header,Django 后端开放可读 Cookie(HttpOnly=False),Nginx 准确透传自定义 Header。三者缺一不可。










