I'm trying to test my Django app locally using SSL. I have a view with the @login_required
decorator. So when I hit /locker
, I get redirected to /locker/login?next=/locker
. This works fine with http.
However, whenever I use https, the redirect somehow drops the secure connection, so I get something like https://cumulus.dev/locker -> http://cumulus.dev/locker/login?next=/locker
If I go directly to https://cumulus.dev/locker/login?next=locker
the page opens fine over a secure connection. But once I enter the username and password, I go back to http://cumulus.dev/locker
.
I'm using Nginx to handle the SSL, which then talks to runserver
. My nginx config is
upstream app_server_djangoapp {
server localhost:8000 fail_timeout=0;
}
server {
listen 80;
server_name cumulus.dev;
access_log /var/log/nginx/cumulus-dev-access.log;
error_log /var/log/nginx/cumulus-dev-error.log info;
keepalive_timeout 5;
# path for static files
root /home/gaurav/www/Cumulus/cumulus_lightbox/static;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
if (!-f $request_filename) {
proxy_pass http://app_server_djangoapp;
break;
}
}
}
server {
listen 443;
server_name cumulus.dev;
ssl on;
ssl_certificate /etc/ssl/cacert-cumulus.pem;
ssl_certificate_key /etc/ssl/privkey.pem;
access_log /var/log/nginx/cumulus-dev-access.log;
error_log /var/log/nginx/cumulus-dev-error.log info;
keepalive_timeout 5;
# path for static files
root /home/gaurav/www/Cumulus/cumulus_lightbox/static;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Ssl on;
proxy_set_header Host $http_host;
proxy_redirect off;
if (!-f $request_filename) {
proxy_pass http://app_server_djangoapp;
break;
}
}
}
Django is running on plain HTTP only behind the proxy, so it will always use that to construct absolute URLs (such as redirects), unless you configure it how to see that the proxied request was originally made over HTTPS.
As of Django 1.4, you can do this using the
SECURE_PROXY_SSL_HEADER
setting. When Django sees the configured header, it will treat the request as HTTPS instead of HTTP:request.is_secure()
will return true,https://
URLs will be generated, and so on.However, note the security warnings in the documentation: you must ensure that the proxy replaces or strips the trusted header from all incoming client requests, both HTTP and HTTPS. Your nginx configuration above does not do that with
X-Forwarded-Ssl
, making it spoofable.A conventional solution to this is to set
X-Forwarded-Protocol
tohttp
orhttps
, as appropriate, in each of your proxy configurations. Then, you can configure Django to look for it using: