Django HTTPS with nginx as reverse proxy. Issues with CSRF tokens

I developed a Django application which runs inside a docker container. Outside the container, a natively running nginx does the reverse proxy-ing of the requests to that django application. Django runs via WSGI with gunicorn.

General setup:

  • I use a HTTP sever on port 80 which only serves a redirect to HTTPS on Port 443. No data is actually served on that port.
  • Port 443 is setup for HTTP/2 with SSL encryption
    • Path /static is served by nginx to supply static django data.
    • Path / is configured as proxy to the django instance:
    location / {
         proxy_set_header Host $http_host;
         proxy_set_header X-Real-IP $remote_addr;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
         proxy_set_header X-Forwarded-Proto $scheme;
         proxy_pass http://127.0.0.1:8000; # This is the port the docker container with my django listens to
    }

Django (version 5.1.3) is configured with the following options:

# Production only settings
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_SSL_REDIRECT = False

I set the SECURE_SSL_REDIRECT to false, as I have a server internal healthcheck that accesses the django application on http://localhost:8000 and checks its status. As there is not HTTPS on that route, a forced SSL redirect would break that check. I rely on the strict configuration of nginx that an outside access can never occur via HTTP (without S). Of course, port 8000 is blocked from the internet and only allowed on localhost connections.

First question: Is that a viable approach for production?

Second question: I mentioned explicitly that I'm using a docker container because I have a friend using the same application on his server. He's using the same docker container. So differences in the django setup are eliminated. However, his server has an older version of nginx.

He is using: nginx/1.14.2. Let's call that the "old setup" I am using: nginx/1.26.2. Let's call that the "new setup"

On the old setup, CSRF tokens are not working due to failed security checks. Including

SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")

in the django configuration solves that issue.

On the new setup on my side, this is not necessary. A classic case of "It runs but I don't know why." I can't figure out why it even works on the "new setup". Anyone has any ideas why this configuration shows different behavior on two setups?

I'm currently hesitant to simply include SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") as I haven't understood the issue. According to the wiki, I think it should be safe with the above described nginx proxy config but I'm not sure.

Back to Top