How to handle secure http (https) via wsgi
October 28, 2008 – 02:53Just 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 loggingfrom 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)