Search code examples
djangonginxreverse-proxy

Django returning "CSRF verification failed. Request aborted. " behind Nginx proxy locally


I'm running a simple Django application without any complicated setup (most of the default, Django allauth & Django Rest Framework).

The infrastructure for running both locally and remotely is in a docker-compose file:

version: "3"

services:
  web:
    image: web_app
    build:
      context: .
      dockerfile: Dockerfile
    command: gunicorn my_service.wsgi --reload  --bind 0.0.0.0:80 --workers 3
    env_file: .env
    volumes:
      - ./my_repo/:/app:z
    depends_on:
      - db
    environment:
      - DOCKER=1

  nginx:
    image: nginx_build
    build:
      context: nginx
      dockerfile: Dockerfile
    volumes:
      - ./my_repo/:/app:z
    ports:
      - "7000:80"

... # db and so on

as you see, I'm using Gunicorn the serve the application and Nginx as a proxy (for static files and Let's Encrypt setup. The Nginx container has some customizations:

FROM nginx:1.21-alpine

RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d

And the nginx.conf file is a reverse proxy with a static mapping:

server {

    listen 80;

    location / {
        proxy_pass http://web;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_redirect off;
    }
    location /static/ {
        alias /app/my_repo/static/;
    }

}

Running this on the server after setting up let's encrypt in the Nginx container works without any issue, but locally I get the "CSRF verification failed. Request aborted." error every time I submit a form (e.g. create a dummy user in Django Admin). I exposed the web port and used it to submit the forms and it worked.

Because of that, I deduce that there is something missing in the Nginx config or something to "tell" Django how to handle it. So, what I'm missing and how should I investigate this?


Solution

  • Since you're using a proxy that translates https requests into http, you need to configure Django to allow POST requests from a different scheme (since Django 4.0) by adding this to settings.py:

    CSRF_TRUSTED_ORIGINS = ["https://yourdomain.com", "https://www.yourdomain.com"]
    

    If this does not solve your problem, you can temporarily set DEBUG = True in production and try again. On the error page, you will see a "Reason given for failure" that you can post here.