Desi Founder @ Work

Django Subdomains

We have added a new feature at See'n'Report. It now provides each user with a personal sub-domain URL. The URL maintains user's profile which lists all the photo reports submitted by the user. For example my See'n'Report profile URL is http://sharjeel.seenreport.com/ .

Adding support for sub-domains in Django is simple but has a few catches. It took me quite some time to get everything working.

The following links provide a quick way to make sub-domains:

rossp.org - Using Subdomains with Django Django Ticket #5022 - Proposed middleware: SubdomainURLsMiddleware

The summary of above links is that you have to put a wildcard entry in your domain DNS so that all your subdomains resolve to the IP on which your site is hosted. Then make sure apache handles all the requests to sub-domains; again using a wildcard in apache's configuration. Then write a middleware which checks the subdomain in request's HTTP Host header and processes it accordingly by either loading a customized URLs pattern or by handling the request with a view with appropriate logic.

This worked well for me and I was quickly able to make the changes so that http://www.seenreport.com/user/sharjeel was available at http://sharjeel.seenreport.com/

There were a few little problems:

Firstly, sessions did not work across the subdomains. If I were logged in www.seenreport.com, I wouldn't be logged in at sharjeel.seenreport.com. This was solved by using setting the SESSION_COOKIE_DOMAIN variable in settings.py:

SESSION_COOKIE_DOMAIN = '.seenreport.com' if not DEBUG else '.localhost'

Secondly, all the main navigation links (login, register etc.) which were written as relative links were now pointing to sharjeel.seenreport.com instead of www.seenreport.com. For instance the login on my profile page became sharjeel.seenreport.com/login instead of www.seenreport.com/login .

I could have hardcoded "www.seenreport.com" with these links to make them absolute URLs but that would have been bad in terms of maintainability. It requires an if-else logic for each link so that it renders accordingly on dev/test machines and the production machine.

I used <base/> tag with href="http://www.seenreport.com/" (for production, and localhost for dev) to make the navigation links relative to main domain. This also changed the profile links. However, in our case, profile links are very few so adding hardcoded absolute URLs alongwith some {{ if }} {{ else }} is viable.

Using base-href everything became well except one. Ajax calls didn't work now. When using <base/> tag, the URLs of your ajax calls become relative to href. In this case when an ajax call is made, a domain other than your current one is contacted, which is disallowed by the browser and following exception is raised:

"Access to restricted URI denied" code: "1012"

To overcome this, I appended following with each ajax call's URL:

location.protocol + '//' + location.host + original_url ...

This made all the ajax calls relative to the current subdomain and made everything work perfectly. Took me quite some time to figure out :)