How to force the use of SSL for some URL of my Django Application?

12,201

Solution 1

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>

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

WSGIScriptAlias / /some/path/django.wsgi.
</VirtualHost>

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>

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

WSGIProcessGroup mydjangosite

WSGIScriptAlias / /some/path/django.wsgi.
</VirtualHost>

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.

Solution 2

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',
        (r'^test/secure/$','test_secure',{'SSL':True}),
    )

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):
                request.IS_SECURE=True
            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]
            else:
                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)

Solution 3

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.

    """

    @wraps(view)
    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)

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

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

    return view_or_redirect
Share:
12,201
Natim
Author by

Natim

Work for Ionyse.com My CV here : http://remy.hubscher.crealio.fr/

Updated on June 05, 2022

Comments

  • Natim
    Natim almost 2 years

    I want to be sure that for some URL of my website, SSL will be use. I saw a lot of answer already on SO.

    Force redirect to SSL for all pages apart from one

    So I think I will use mod_rewrite.

    My question is more about how to configure the Virtual Host to run my Django Application over HTTP and over HTTPS without problems. I am using WSGI.

    Is it a problem to just duplicate the configuration over *:443 and over *:80? What should I do to have the best configuration?