[Solved]-How to force the use of SSL for some URL of my Django Application?


If by WSGI you actually mean Apache/mod_wsgi, then although mounted WSGI applications normally get run in their own sub interpreters, the 80/443 split is a special case and even though in different VirtualHost so long as mount point for WSGIScriptAlias, and the ServerName are the same, they will be merged.

<VirtualHost *:80>
ServerName www.example.com

WSGIScriptAlias / /some/path/django.wsgi.

<VirtualHost *:443>
ServerName www.example.com

WSGIScriptAlias / /some/path/django.wsgi.

This will happen for daemon mode as well, but with daemon mode you need to define only a single daemon process group in first VirtualHost definition and then just refer to that from both with WSGIProcessGroup.

<VirtualHost *:80>
ServerName www.example.com

WSGIDaemonProcess mydjangosite ...
WSGIProcessGroup mydjangosite

WSGIScriptAlias / /some/path/django.wsgi.

<VirtualHost *:444>
ServerName www.example.com

WSGIProcessGroup mydjangosite

WSGIScriptAlias / /some/path/django.wsgi.

The WSGIProcessGroup can only reach across like to that VirtualHost for same ServerName.

Django provides a is_secure() method for determining when request came via HTTPS which derives from WSGI variable with request called ‘wsgi.url_scheme’ which is set by mod_wsgi.

So, you would have one single Django WSGI script file and settings file. You just need to duplicate application mounting as decsribed in Apache/mod_wsgi configuration.


Besides using mod_rewrite, you can also use Django to control the SSL redirects.

Here’s a modified version of a middleware from the Satchmo Project. I tend to like this method better than mod_rewrite as it’s easier to manage.

To use it, pass ‘SSL’:True into your url conf:

    urlpatterns = patterns('some_site.some_app.views',

Here’s the middleware code:

    from django.conf import settings
    from django.http import HttpResponseRedirect, get_host

    SSL = 'SSL'

    def request_is_secure(request):
        if request.is_secure():
            return True

        # Handle forwarded SSL (used at Webfaction)
        if 'HTTP_X_FORWARDED_SSL' in request.META:
            return request.META['HTTP_X_FORWARDED_SSL'] == 'on'

        if 'HTTP_X_SSL_REQUEST' in request.META:
            return request.META['HTTP_X_SSL_REQUEST'] == '1'

        return False

    class SSLRedirect:
        def process_request(self, request):
            if request_is_secure(request):
            return None

        def process_view(self, request, view_func, view_args, view_kwargs):          
            if SSL in view_kwargs:
                secure = view_kwargs[SSL]
                del view_kwargs[SSL]
                secure = False

            if settings.DEBUG:
                return None

            if getattr(settings, "TESTMODE", False):
                return None

            if not secure == request_is_secure(request):
                return self._redirect(request, secure)

        def _redirect(self, request, secure):
            if settings.DEBUG and request.method == 'POST':
                raise RuntimeError(
                """Django can't perform a SSL redirect while maintaining POST data.
                    Please structure your views so that redirects only occur during GETs.""")

            protocol = secure and "https" or "http"

            newurl = "%s://%s%s" % (protocol,get_host(request),request.get_full_path())

            return HttpResponseRedirect(newurl)


Here’s a view decorator that you can apply to the views that should have HTTPS.

from functools import wraps
from django.conf import settings
from django.http import HttpResponseRedirect

def require_https(view):
    """A view decorator that redirects to HTTPS if this view is requested
    over HTTP. Allows HTTP when DEBUG is on and during unit tests.


    def view_or_redirect(request, *args, **kwargs):
        if not request.is_secure():
            # Just load the view on a devserver or in the testing environment.
            if settings.DEBUG or request.META['SERVER_NAME'] == "testserver":
                return view(request, *args, **kwargs)

                # Redirect to HTTPS.
                request_url = request.build_absolute_uri(request.get_full_path())
                secure_url = request_url.replace('http://', 'https://')
                return HttpResponseRedirect(secure_url)

            # It's HTTPS, so load the view.
            return view(request, *args, **kwargs)

    return view_or_redirect


We used some simple middleware to check urls against a list of base urls that must be in HTTPS mode, all others are forced to HTTP mode. The big caveat here is that any POST data can be lost unless you take extra care (it didn’t matter in our case). We were doing this on join pages that required credit card numbers and the like so as soon as they were in that pipeline we forced them into HTTPS.

Leave a comment