Search code examples
djangohttp-redirectnginxhttpsdjango-admin-filters

How to prevent Django admin from adding changelist filters twice to the URL with active 301 redirect to https?


Django 1.8.3 on nginx setup that redirects admin pages to HTTPS:

upstream project_pass {
    server 127.1.1.1:0000;
}

server {
    listen          443;
    server_name     subdomain.domain.ext;

    ssl on;

    ...

    location / {
            uwsgi_pass      project_pass;
            include         uwsgi_params;
    }
}

server {
    listen          80;
    server_name     subdomain.domain.ext;

    location / {
            rewrite ^ https://$host$request_uri permanent;
    }
}

After activating any filter on the object list page it is stored in the _changelist_filters GET variable and added to the form action on object edit page. But at least on Firefox the form is at first submitted to HTTP somehow, gets redirected to the HTTPS and somewhere in the process GET filters are set two times, like this:

https://domain.ext/admin/project/model/?status__exact=new?status__exact=new

I cannot nor find any similar bugs that are already created neither create one on the Django Trac (please do it if you can confirm the problem).

What can I do to prevent these double filters (they make admin usage quite uncomfortable) and still have all /admin requests redirected to HTTPS?

P.S. Running server with runserver is not affected by this problem, obviously.


Solution

  • You need to set the http headers correctly (most importantly X-Forwarded-Proto), otherwise django doesn't know that the first request is an https request, and assumes it's http, so all the links it creates are http links.

    location / {
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header Host $http_host;
            uwsgi_pass      project_pass;
            include         uwsgi_params;
    }
    

    This would prevent the http links. Also, your rewrite command should have a question mark at the end, so nginx won't add the GET arguments twice:

    location / {
            rewrite ^ https://$host$request_uri? permanent;
    }
    

    You might want to consider using the simpler return directive.

    location / {
            return 301 https://$host$request_uri;
    }