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?
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.
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)
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
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.