I have a Django project working locally with login to the admin portal working. Once the project has been deployed to our development environment the pages that do not require CSRF authentication are viewable, but the admin portal returns a CSRF token error when attempting to login.
Error received: CSRF cookie not set
The project is meant to be deployed to a subdomain (ie https://project.myapp.com) with the development/staging version deployed to a different subdomain (https://project.dev.myapp.com).
Subset of the 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',
)
Originally I assumed that the issue was due to the project being deployed on a subdomain and followed the documentation for csrf and added the CSRF_TRUSTED_ORIGINS = ['https://*.myapp.com']
to the settings but the issue persists.
I also tried setting the CSRF_COOKIE_DOMAIN
setting to either .myapp.com
or to .dev.myapp.com
but continue to see the same issue.
I had also seen people mention the django.template.context_processors.csrf
option to add to the TEMPLATES context_processors and tried that, but this also had no effect.
When looking at the request to the /admin/login view, a csrfmiddlewaretoken
is included, but nothing is being set in the browser.
Is there a different setting I’m missing?
P.S.
The deployment is run with uwsgi behind nginx but since the pages are all still served properly I’m (maybe incorrectly) assuming the issue doesn’t lie there.
Edit:
Adding the NGINX config files being used – im not very well versed in NGINX but I don’t believe anything would be stripping the cookie header.
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
2
Answers
The issue actually lied with the deployment itself. The server is sitting behind an AWS cloudfront distribution which was not forwarding the cookie header to the server. @malvin's comment about logging the cookie in the NGINX logs was what lead to the discovery.
Maybe you could make
SESSION_COOKIE_SECURE
andCSRF_COOKIE_SECURE
flags are matching. You’ve setCSRF_COOKIE_SECURE = False
. IfSESSION_COOKIE_SECURE
is True, then it might be causing the issue. You should either set both flags to be False during development or set them both True for a production HTTPS environment.For CSRF Cookie Domain, when you move to a new subdomain, the period
.
at the start is crucial. This ensures all sub-domains are included.And, it might be beneficial to ensure that your NGINX configuration isn’t stripping out the "Cookie" header for some reason. The CSRF Token mechanism in Django works by comparing a value stored in your session cookie to a value sent in a hidden form field or HTTP header. If your NGINX configuration isn’t properly forwarding along the Cookie header, this could cause the issue.