[Django]-Django CSRF validation fails on POST requests : referer checking failed – no Referer

3👍

AFAIK, This is the purpose of CSRF, to avoid posting data from unknown strange sources. You need csrf token to post this which django generates dynamically.

0👍

Upgrading Django might fix the missing Referer error.

As of Django 4.0 (release notes), the backend will first check the Origin header before falling back to the Referer header (source):

  1. CsrfViewMiddleware verifies the Origin header, if provided by the browser, against the current host and the CSRF_TRUSTED_ORIGINS setting. This provides protection against cross-subdomain attacks.
  2. In addition, for HTTPS requests, if the Origin header isn’t provided, CsrfViewMiddleware performs strict referer checking. This means that even if a subdomain can set or modify cookies on your domain, it can’t force a user to post to your application since that request won’t come from your own exact domain.

0👍

It’s possible you have a reverse proxy running, for example an nginx proxy_pass to 127.0.0.1:8000?

In this case, Django expects the Cross-Site Forgery Protection tokens to match hostname 127.0.0.1, but they will be coming from a normal domain (for example example.com).

Expected Source Actual Source
http://127.0.0.1 https://example.com

HTTP reverse proxy (example.com:80 -> localhost:3000) is a common way to use nginx with NodeJS applications, but it doesn’t work well with Django

Client-Facing URL Server Proxy URL
https://example.com http://127.0.0.1:3000

It is better to run Django through a Unix socket rather than a port (example.com:80 -> <socket>). You can do this with Gunicorn:

Client-Facing URL Server Proxy URL
https://example.com unix:/run/example.com.sock

Here’s how to do this with Django, Gunicorn, and nginx:

Let’s say you’ve got a Django project root, which contains a system folder (the one where settings.py and wsgi.py are):

export DJANGO_PROJECT_PATH=/path/to/django/root
export DJANGO_SETTING_FOLDER=system

First, make sure you have Gunicorn installed and that you are using a virtual environment:

cd $DJANGO_PROJECT_PATH
source .venv/bin/activate  # <- Use a virtual environment
pip3 install gunicorn  # <- install Gunicorn in the venv

Run Gunicorn. This will start the Django project similar to running python3 manage.py runserver, except that you can listen for requests on a Unix socket:

$DJANGO_PROJECT_PATH/.venv/bin/gunicorn  \
    --workers=3 \
    --access-logfile - \
    --bind unix:/run/example.com.sock \  # <- Socket
    --chdir=$DJANGO_PROJECT_PATH/ \
    $DJANGO_SETTING_FOLDER.wsgi:application

Then create an HTTP proxy using nginx that passes HTTP requests from clients through the gunicon-created socket:

/etc/nginx/sites-enabled/example.com:

server {
    listen 80;
    listen [::]:80;
    server_name example.com;

    # serve static files directly through nginx
    location /static/ {
        autoindex off;
        root /path/to/django/root;
    }

    # serve user-uploaded files directly through nginx
    location /media/ {
        autoindex off;
        root  /path/to/django/root;
    } 

    # You can do fun stuff like aliasing files from other folders
    location /robots.txt {
        alias /path/to/django/root/static/robots.txt;
    }

    # here is the proxy magic
    location / {
        include proxy_params;
        proxy_pass http://unix:/run/example.com.sock;  # <- the socket!
    }
}

Make sure to restart nginx:

sudo service restart nginx

After all this, your csrf tokens should match the domain name of your site and you’ll be able to log in and submit forms.

Leave a comment