Skip to content

Django domain redirect middleware

Most web developers know it’s possible to serve one site on several domains or subdomains, using eg. the ServerAlias Apache directive. Sometimes this behaviour is not wanted: you want your main domain to be “foo.tld”, although “” should work too, and maybe even some completely different domains.

This way it’s possible to have permalinks, and you won’t get bad points from search engine spiders who don’t like the same content to be available on several URIs.

In Django there’s the PREPEND_WWW setting, which will force all requests to be redirected to when coming in on, etc. This functionality is rather limited though. As I wanted to be able to have one unique main domain in my new application, I wrote a middleware which accomplishes this in a very generic way, using the django.contrib.sites framework. You need to add this middleware before all others in your settings file, even before the CommonMiddleware.

Here’s the code:

from django.contrib.sites.models import Site
from django.http import HttpResponsePermanentRedirect
from django.core.urlresolvers import resolve
from django.core import urlresolvers
from django.utils.http import urlquote

class DomainRedirectMiddleware(object):
    def process_request(self, request):
        host = request.get_host()
        site = Site.objects.get_current()

        if host == site.domain:
            return None

        # Only redirect if the request is a valid path
            # One issue here: won't work when using django.contrib.flatpages
            # TODO: Make this work with flatpages :-) 
        except urlresolvers.Resolver404:
            return None

        new_uri = '%s://%s%s%s' % (
                request.is_secure() and 'https' or 'http',
                (request.method == 'GET' and len(request.GET) > 0) and '?%s' % request.GET.urlencode() or ''

        return HttpResponsePermanentRedirect(new_uri)

Make sure you got the sites framework set up correctly when using this! As noted in the code, this doesn’t work with the Flatpages framework yet, this is TODO.

Posted in Development.

Tagged with , .

7 Responses

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.

  1. Vincent van Adrighem says

    Why not use virtual hosts with rewriterules?
    If you’re using apache anyway, then this is the most elegant solution IMHO.
    You want every request to go to, but used to have, then set up a virtual host on containing nothing but a rewriterule:

    RewriteEngine On
    RewriteRule ^(.*)$$1 [R=permanent]

    That way, client software even gets a 301 signal, giving the browser the possibility to automatically update your bookmarks.

  2. Nicolas says

    Vincent: my solution also uses a HTTP 301 (a HttpResponsePermanentRedirect also translates to a HTTP 301 reply). One of the main benefits is it doesn’t force you to use multiple vhosts, which is not always possible in shared hosting environments.

    By the way, your solution could be simplified by using something like

    Redirect permanent /

    in your Apache configuration, instead of pulling in mod_rewrite ;-)

  3. Martin says

    I just used your solution, it works flawlessly. Thanks !

    I know I could have used apache mod_rewrite instead, but I felt more secure with your solution, since I understand django/python much better.

  4. Sagi says

    The forum is a bihrtger place thanks to your posts. Thanks!

  5. trd says

    Does the last solution of Nicolas keep the whole URL?

    For example if I’m poiting to, I want to redirect to, right?

  6. Nicolas says

    Thanks a lot!
    If you’re running Django on a non-default port, you might want to add the port number to the newly constructed URI:

    new_uri = ‘%s://%s%s%s%s’ % (
    request.is_secure() and ‘https’ or ‘http’,
    (“:” + request.META["SERVER_PORT"]) if request.META["SERVER_PORT"]!=80 else “”,
    (request.method == ‘GET’ and len(request.GET) > 0) and ‘?%s’ % request.GET.urlencode() or ”

  7. Nicolas says

    oh and say host = request.get_host().split(‘:’)[0] above there

Some HTML is OK

or, reply to this post via trackback.