News, ideas and randomness

Requiring https for certain paths in Django

Posted: February 6th, 2010 | Author: Scott Barnham | Filed under: Django | 2 Comments »

A while ago I wrote about Securing Django with SSL. Here’s a small addition.

Some paths need https

If you’re using SSL it makes sense for certain parts of the site to require a secure connection. For example, the admin section.

Previously I shared the secure_required decorator which forces requests to use https for specific views. This works ok, but if you know an entire section of the site under a given path (e.g. /admin/) should be secure, it’s hassle to have to add the decorator to each view.

You can require secure connections over https using webserver config or using Django itself.

Requiring https using Nginx

In your Nginx config file under the section for the unsecure http/port 80 server you can specify a location path and redirect all requests to it to https instead.

server {
    listen 10.10.10.10:80;
    server_name example.com;
...
    location /admin {
        # force admin to use https
        rewrite (.*) https://example.com/$1 permanent;
    }
...
}

Apache and other web servers can have a similar configuration.

If you can configure it in the web server, that’s more efficient because the request can be redirected by the server, without having to contact your Django project. However, it should be fairly rare for requests to be redirected like this so it’s not a big performance issue and sometimes it’s easier to handle things in Django.

Requiring https using Django middleware

In Django it’s easy to write custom middleware which gets called before each request reaches a view.

Here’s a small piece of middleware which checks if the request is over http to a path we want to be secure and if so redirects to the same path but over https.

from django.http import HttpResponsePermanentRedirect
from django.conf import settings

class SecureRequiredMiddleware(object):
    def __init__(self):
        self.paths = getattr(settings, 'SECURE_REQUIRED_PATHS')
        self.enabled = self.paths and getattr(settings, 'HTTPS_SUPPORT')

    def process_request(self, request):
        if self.enabled and not request.is_secure():
            for path in self.paths:
                if request.get_full_path().startswith(path):
                    request_url = request.build_absolute_uri(request.get_full_path())
                    secure_url = request_url.replace('http://', 'https://')
                    return HttpResponsePermanentRedirect(secure_url)
        return None

In settings.py

MIDDLEWARE_CLASSES = (
...
    'myproject.middleware.SecureRequiredMiddleware',
)

HTTPS_SUPPORT = True
SECURE_REQUIRED_PATHS = (
    '/admin/',
    '/accounts/',
    '/management/',
)

SECURE_REQUIRED_PATHS is a list or tuple of paths that should be secure. Any request to a path which starts with one of these will be required to use https.

HTTPS_SUPPORT is a custom setting to make it easier to use this on your dev server without SSL support. Set it to True in the settings for the live server and False in the settings for the dev server.

So there we go, an easy way to require secure https requests for certain parts of your Django site.


2 Comments on “Requiring https for certain paths in Django”

  1. 1 Diego said at 2:28 am on December 11th, 2013:

    Great! It works… thanks a lot

  2. 2 Casey said at 4:38 pm on March 22nd, 2014:

    I know this is an old post, but I’ve used functionality like this in the past and I’d like to offer a suggestion.

    I prefer to implement SSL paths with decorators on views. That way your secure paths are tied to your business logic instead of explicit URLs. Defining URL paths in settings invalidates the ability to programatically reverse those URLs should they ever change in your urlconf.

    See this example:
    https://gist.github.com/ckinsey/9709984

    Thanks!


Leave a Reply