Secure your cookies
In settings.py put the lines
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
and cookies will only be sent via HTTPS connections. Additionally, you probably also want SESSION_EXPIRE_AT_BROWSER_CLOSE=True. Note if you are using older versions of django (less than 1.4), there isn't a setting for secure CSRF cookies. As a quick fix, you can just have CSRF cookie be secure when the session cookie is secure (SESSION_COOKIE_SECURE=True), by editing django/middleware/csrf.py:
def process_response(self, request, response):
request.META["CSRF_COOKIE"], max_age = 60 * 60 * 24 * 7 * 52,
secure=settings.SESSION_COOKIE_SECURE or None)
Direct HTTP requests to HTTPS in the webserver
Next you want a rewrite rule that redirects http requests to https, e.g., in nginx
rewrite ^(.*) https://$host$1 permanent;
Django's reverse function and url template tags only return relative links; so if you are on an https page your links will keep you on the https site.
Set OS environmental variable HTTPS to on
Finally, (and my original response excluded this), you need to enable the OS environmental variable HTTPS to 'on' so django will prepend https to fully generated links (e.g., like with HttpRedirectRequests). If you are using mod_wsgi, you can add the line:
os.environ['HTTPS'] = "on"
to your wsgi script. If you are using uwsgi, you can add an environmental variable by the command line switch --env HTTPS=on or by adding the line env = HTTPS=on to your uwsgi .ini file. As a last resort if nothing else works, you could edit your settings file to have the lines import os and os.environ['HTTPS'] = "on", which also should work.
If you are using wsgi, you may want to additionally set the environmental variable wsgi.url_scheme to 'https' by adding this to your settings.py :
os.environ['wsgi.url_scheme'] = 'https'
The wsgi advice courtesy of Vijayendra Bapte's comment.
You can see the need for this environmental variable by reading django/http/__init__.py:
def build_absolute_uri(self, location=None):
Builds an absolute URI from the location and the variables available in
this request. If no location is specified, the absolute URI is built on
if not location:
location = self.get_full_path()
if not absolute_http_url_re.match(location):
current_uri = '%s://%s%s' % (self.is_secure() and 'https' or 'http',
location = urljoin(current_uri, location)
return os.environ.get("HTTPS") == "on"
Additional Web Server Things:
Take that guy's advice and turn on HSTS headers in your web server by adding a line to nginx:
add_header Strict-Transport-Security max-age=31536000;
This tells your web browser that your website for the next 10 years will be using HTTPS only. If there's any Man-in-the-middle attack on any future visit from the same browser (e.g., you log on to a malicious router in a coffee-shop that redirects you to an HTTP version of the page), your browser will remember it is supposed to be HTTPS only and prevent you from inadvertently giving up your information. But be careful about this, you can't change your mind and later decide part of your domain will be served over HTTP (until the 10 years have passed from when you removed this line). So plan ahead; e.g., if you believe your application may soon grow in popularity and you'll need to be on a big CDN that doesn't handle HTTPS well at a price you can afford, you may have an issue.
Also make sure you disable weak protocols. Submit your domain to an SSL Test to check for potential problems (too short key, not using TLSv1.2, using broken protocols, etc.). E.g., in nginx I use:
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS";