在生产环境中,用 Nginx 代理 Django 出现 403 CSRF 验证错误,根源通常在于 Nginx 未能正确将原始请求的信息(尤其是协议是 HTTP 还是 HTTPS)传递给 Django,导致 Django 的请求校验失败。
要解决这个问题,核心是确保 Django 能“看”到真实的请求,而不是 Nginx 转发后的样子。这需要你同时修改 Django 的 settings.py 和 Nginx 的配置文件。
🐍 第一步:配置 Django (settings.py)
在 Django 的配置文件 settings.py 中,你需要添加或修改以下三个关键配置项。
1. 设置受信任的来源 (CSRF_TRUSTED_ORIGINS)
这是最直接的方法。它明确告诉 Django,来自这些域名的请求是安全的,直接放行通过 CSRF 验证。
# settings.py # 明确指定所有前端可访问的域名,包括协议 CSRF_TRUSTED_ORIGINS = [ 'https://your-domain.com', # 你的正式域名 'https://www.your-domain.com', # 如果存在,也加上带 www 的 # 'http://test.your-domain.com', # 如果还有其他子域名 ]
说明:请将
your-domain.com替换为你的实际域名。如果你的站点启用了 HTTPS,配置中必须包含https://前缀。注意:虽然设置通配符
*可能暂时绕过问题,但为了安全考虑,不推荐这样做。
2. 配置代理 SSL 头 (SECURE_PROXY_SSL_HEADER)
如果你的站点配置了 HTTPS,且 Nginx 在内部是通过 HTTP 与 Django 通信的,那么这个设置至关重要。
# settings.py
# 告诉 Django 信任来自 Nginx 的 X-Forwarded-Proto 头信息
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')原理:当 Nginx 将 HTTPS 请求转发给 Django 时,会带上一个自定义的 Header:
X-Forwarded-Proto: https。这个设置让 Django 知道,尽管收到的内部请求是 HTTP,但原始请求是安全的 HTTPS,从而通过 CSRF 的检查。重要安全提醒:配置此项后,务必确保 Nginx 是 Django 应用唯一能接触到的外部代理,并配置好防火墙,防止外部请求伪造这个 Header 绕过验证。
3. 启用安全 Cookie (CSRF_COOKIE_SECURE)
此设置强制 CSRF 的 Cookie 只能通过 HTTPS 连接传输,是提升安全性的关键一步。
# settings.py # 确保 CSRF 的 Cookie 仅通过 HTTPS 发送 CSRF_COOKIE_SECURE = True
说明:启用后,如果 Django 误判了请求为 HTTP,它将不会设置或验证 CSRF Cookie,直接导致 403 错误,这能反向验证你的 SECURE_PROXY_SSL_HEADER 是否配置正确。
🚀 第二步:配置 Nginx (nginx.conf)
接下来,需要在 Nginx 的配置文件的 location 块中,修改或添加一些关键的 proxy_set_header 指令,以确保 Django 能获取到正确的信息。
# /etc/nginx/sites-available/your-site 或 nginx.conf
server {
listen 80;
server_name your-domain.com www.your-domain.com;
# 将 HTTP 请求重定向到 HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
server_name your-domain.com www.your-domain.com;
# SSL 证书配置 (请替换为你的实际路径)
ssl_certificate /path/to/your/certificate.crt;
ssl_certificate_key /path/to/your/private.key;
# 其他 SSL 配置(如 ssl_protocols 等)...
location / {
# 将请求转发给 Django 应用服务器 (如 Gunicorn)
proxy_pass http://127.0.0.1:8000; # 将端口替换为你应用的端口
# --- 核心配置:传递原始请求信息 ---
proxy_set_header Host $host; # 传递原始 Host
proxy_set_header X-Real-IP $remote_addr; # 传递真实客户端 IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; # 传递原始协议 (http/https)
# 其他 proxy 配置(可选)
proxy_redirect off;
# ...
}
# 静态文件处理 (可选)
location /static/ {
alias /path/to/your/staticfiles/;
}
}proxy_set_header Host $host;:这是非常关键的一步。它确保传递给 Django 的Host头是用户访问的原始域名,而不是 Nginx 内部转发的地址。
配置完成后,务必运行 sudo nginx -t 测试配置是否正确,然后执行 sudo systemctl reload nginx 或 sudo nginx -s reload 使配置生效。
🩺 第三步:验证与排查
配置完成后,可以通过以下方式验证。
检查 Django 日志:启用
DEBUG = True或在日志中查看详细的 403 错误报告,它通常会指明是哪个Origin检查失败,这能帮你快速定位问题。浏览器开发者工具:打开“网络”(Network) 标签页,检查请求的
Cookie头是否包含csrftoken,以及Host和Origin头是否与你的CSRF_TRUSTED_ORIGINS设置匹配。临时放宽限制(仅用于测试):为验证是否是配置问题,可以临时注释掉
CSRF_COOKIE_SECURE = True,或临时将CSRF_TRUSTED_ORIGINS = ['*'],如果此时能正常访问,则表明问题确凿在之前的配置上。测试完后必须立即恢复!
希望这份指南能帮你顺利解决问题。在生产环境部署时,请务必仔细核对每一项配置,以确保应用的安全与稳定。