How to handle secure http (https) via wsgi

October 28, 2008 – 02:53

Just a little note… If you happen to run django + wsgi + apache and want to access your site via https, you will definitely need this.

Problem: A site, deployed on client’s server redirects all https requests to http.

Initially we’ve thought that a misconfigured apache is to be blamed.

It appeared that not.

Looks like that django wsgi handler under some configurations can’t decide if incoming connection is secure. You have to pass that info explicitly (e.g. via a header) and modify your wsgi handler.

We’ve based this code snippet on a fairview computing post:

import logging

from django.conf import settings import django.core.handlers.wsgi

class WSGIRequest(django.core.handlers.wsgi.WSGIRequest): """ WSGIRequest subclass for use behind a proxy that handles SSL.

It checks for a header indicating whether a request should be
considered secure. By default, it looks for 'X-Forwarded-Proto'
(which will appear in request.META as 'HTTP_X_FORWARDED_PROTO')
and expects a value of 'http' or 'https'. All string checks are
case-insensitive. You can configure its behavior with these 
settings:

FC_WSGI_PROTOCOL_HEADER: the name of the header to check.
FC_WSGI_PROTOCOL_HTTPS_VALUE: the value to expect on HTTPS requests
FC_WSGI_PROTOCOL_HTTP_VALUE: the value to expect on HTTP requests

You should make sure that the front-end proxy scrubs the header; 
it should not be possible for a client to send the header on a 
plaintext connection and have it reach the back-end server.

To use this, simply replace the stock Django WSGIHandler in your 
WSGI script. Here's a complete example:

import os
import sys

sys.path.append("/usr/local/django/apps")

sys.path.append("/usr/local/django/sites")

os.environ['DJANGOSETTINGSMODULE'] = 'yourapp.settings'

os.umask(0007)

_application = django.core.handlers.wsgi.WSGIHandler()

def application(environ, start_response):
    environ['PATH_INFO'] = environ['SCRIPT_NAME'] + environ['PATH_INFO']
    if environ.get('HTTPS','off') in ('on','1'):
        environ['wsgi.url_scheme'] = 'https'
    else:
        environ['wsgi.url_scheme'] = 'http'
    if environ['wsgi.url_scheme'] == 'https':
            environ['HTTPS'] = 'on'

    return _application(environ, start_response)

"""

def is_secure(self):
    logger = logging.getLogger('fairview.wsgi.WSGIRequest.is_secure')
    header = getattr(settings, 
                        'FC_WSGI_PROTOCOL_HEADER',
                        'HTTP_X_FORWARDED_PROTO'
                        )

    https_value = getattr(settings,
                            'FC_WSGI_PROTOCOL_HTTPS_VALUE',
                            'https'
                            ).lower()

    http_value = getattr(settings,
                            'FC_WSGI_PROTOCOL_HTTP_VALUE',
                            'http'
                            ).lower()
    value = self.META.get(header, '').lower()

    if settings.DEBUG:
        logger.debug("""HEADER: '%s' HTTPS value: '%s' HTTP value: '%s' request value: '%s'""" % (header, https_value, http_value, value))
    if value == https_value:
        if settings.DEBUG:
            logger.debug("""Request is secure.""")
        return True

    if settings.DEBUG:
        logger.debug("""Request is insecure.""")
    return False

class WSGIHandler(django.core.handlers.wsgi.WSGIHandler): request_class = WSGIRequest

_application = WSGIHandler()

def application(environ, startresponse): environ['PATHINFO'] = environ['SCRIPTNAME'] + environ['PATHINFO'] if environ.get('HTTPS','off') in ('on','1'): environ['wsgi.urlscheme'] = 'https' else: environ['wsgi.urlscheme'] = 'http' if environ['wsgi.url_scheme'] == 'https': environ['HTTPS'] = 'on'

return _application(environ, start_response)

  • http://tumbletooth.org bayuadji

    hi,

    nice info, I’ve been looking how to manage django https for a couple a days :)

  • http://blog.dscpl.com.au Graham Dumpleton

    FWIW, the way they did it was more complicated than it needed to be. See simpler approach in comment to their blog. Also see:

    http://groups.google.com/group/modwsgi/browse_frm/thread/94f952720c878506

    The real question though is if there was no front end server like nginx involved, what was the Apache configuration that was being used. It seems rather odd that https was being redirected to http addresses. Depending on why they are doing this, it could still be regarded as a misconfiguration.

  • http://removeripoffreports.net/ onlinereputation_management

    It checks for a header indicating whether a request should be
    considered secure. By default, it looks for 'X-Forwarded-Proto'
    (which will appear in request.META as 'HTTPXFORWARDED_PROTO')
    and expects a value of 'http' or 'https'. All string checks are
    case-insensitive. You can configure its behavior with these 
    settings: