部署到子域的Django Admin登录提示CSRF cookie未设置问题排查
本地Django项目的Admin后台登录功能正常,但部署到开发环境子域https://project.dev.myapp.com(生产域为https://project.myapp.com)后,无需CSRF认证的页面可正常访问,唯独Admin登录时返回CSRF cookie not set错误。查看/admin/login请求发现,表单已携带csrfmiddlewaretoken,但浏览器并未设置CSRF Cookie。
现有配置
Django settings.py 关键片段
ALLOWED_HOSTS = ['*'] CSRF_COOKIE_SECURE = False INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.gis', 'django.contrib.messages', 'django.contrib.sessions', 'django.contrib.staticfiles', 'django_object_actions', ] MIDDLEWARE = ( 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', )
已尝试的无效操作
- 添加
CSRF_TRUSTED_ORIGINS = ['https://*.myapp.com'] - 设置
CSRF_COOKIE_DOMAIN为.myapp.com或.dev.myapp.com - 向TEMPLATES的context_processors添加
django.template.context_processors.csrf
Nginx相关配置
nginx.conf
user nginx; worker_processes auto; error_log /dev/stderr warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$realip_remote_addr [$time_local] "$request" ' '$status $body_bytes_sent $request_time ' '"$http_referer" "$http_user_agent" ' '"$http_x_amzn_trace_id" "$http_x_amz_cf_id"'; access_log /dev/stdout main; sendfile on; #tcp_nopush on; keepalive_timeout 65; #gzip on; #maximum file size client_max_body_size 0; include /etc/nginx/conf.d/*.conf; }
server.conf
upstream myapp { server 127.0.0.1:8000; } server { listen 7999 default_server; server_name $GENERAL_SUBDOMAIN.$GENERAL_DOMAIN; include /etc/nginx/conf.d/myapp.params; # Enable RealIP real_ip_header X-Forwarded-For; real_ip_recursive on; set_real_ip_from 0.0.0.0/0; uninitialized_variable_warn off; resolver 8.8.8.8; location /static/admin { uwsgi_pass myapp; include uwsgi_params; uwsgi_read_timeout 600; uwsgi_send_timeout 600; uwsgi_ignore_client_abort on; expires $static_expires; } location /static { root /app; expires $static_expires; } location / { uwsgi_pass myapp; include uwsgi_params; uwsgi_read_timeout 600; uwsgi_send_timeout 600; uwsgi_ignore_client_abort on; expires $static_expires; } } # vim: ft=nginx ts=2 sw=2
myapp.params
error_page 497 https://$best_http_host:$server_port$request_uri; # vim: ft=nginx ts=2 sw=2
排查与解决步骤
修正HTTPS环境下的Cookie安全配置
开发环境使用HTTPS,但CSRF_COOKIE_SECURE = False会导致浏览器拒绝保存Cookie(HTTPS页面默认要求Cookie标记为Secure)。修改settings.py:CSRF_COOKIE_SECURE = True SESSION_COOKIE_SECURE = True传递正确的协议头给Django
Django需要识别请求为HTTPS,否则不会发送Secure Cookie。在Nginx的location /和/static/admin块中添加:uwsgi_param X-Forwarded-Proto $scheme;同时在
settings.py中开启代理SSL头识别:SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')调整Cookie的SameSite属性
现代浏览器对跨子域Cookie的SameSite属性有严格限制,添加以下配置到settings.py:CSRF_COOKIE_SAMESITE = 'Lax' SESSION_COOKIE_SAMESITE = 'Lax'若涉及跨域场景可设为
'None',但必须配合Secure=True使用。检查Nginx是否拦截Cookie头
确认Nginx配置中没有过滤、修改响应头的规则,保证Django发送的CSRF Cookie能完整传递到浏览器。清除浏览器缓存与Cookie
之前的错误Cookie配置可能残留,手动清除后重新测试登录。
内容的提问来源于stack exchange,提问作者Buns

