Inércia Sensorial


Apache + mod_wsgi + Django + lighttpd

Filed under: Perils of Software Development — Tags: , , , , — inerte @ 18:45

I’ve written how to configure Apache, mod_python and Django and how to put lighttpd behind Apache.

Recently I decided to host my most visited website on a different VPS provider[1], and started a quest to update my knowledge about Django deployment. I did things differently this time, using mod_wsgi (the recommended way of deploying Apache and Django), and configuring Apache behind lighttpd for dynamic content (in other words, lighttpd will serve static media).

I did everything below in the last couple days, and did not wrote things as I was doing it, because it involved a lot of experimentation (trial and error) for me. As such, I am writing this article based on memory and checking my config files. If you encounter any problems, please leave a comment and I will clarify any omissions.

Here’s how to do it:

Install the usual suspects

I choose Ubuntu as my Linux distro, and installation of anything is a breeze on it. sudo apt-get install package-name. This part is well covered around the web, so I won’t comment in details how it’s done. Sufficient to say, some of the packages I’ve installed were apache2, libapache2-mod-wsgi, and lighttpd.

Configure Apache and mod_wsgi to load your project

Since lighttpd will act as the primary server for my domain, I decided to move Apache to port 81:

sudo vi /etc/apache2/ports.conf

Overwrite existing ip:port lines with these:

Listen 81

Where you put your own Python modules on newer Ubuntu installations has changed to /usr/local/lib/python2.6/dist-packages/. Therefore, I’ve uploaded Django, my project and other necessary modules (which weren’t installed by apt-get) to this directory, leaving me with the following structure:


The mod_wsgi documentation has an excellent article on Django integration, but it’s fairly length. You should read it anyway, since there are lots of options that you might want to use. Here’s a cheatsheet:

Create a document root for your domain name:

sudo mkdir /var/www/

Create the file which will be loaded by mod_wsgi with your project configuration:

sudo mkdir /usr/local/lib/python2.6/dist-packages/project_name/apache/
sudo vi /usr/local/lib/python2.6/dist-packages/project_name/apache/django.wsgi

With these contents:

import sys
import os

os.environ[‘DJANGO_SETTINGS_MODULE’] = ‘project_name.settings’

import django.core.handlers.wsgi

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

Create a domain configuration file for Apache:

sudo vi /etc/apache2/sites-available/

With these contents:

ServerAdmin [email protected]

DocumentRoot /var/www/

Alias /media/ /usr/local/lib/python2.6/dist-packages/django/contrib/admin/media/
<Directory /usr/local/lib/python2.6/dist-packages/django/contrib/admin/media>
Options -Indexes
Order deny,allow
Allow from all

Alias /project_media_dir/ /usr/local/lib/python2.6/dist-packages/project_name/templates/project_media_dir/
<Directory /usr/local/lib/python2.6/dist-packages/project_name/templates/project_media_dir>
Options -Indexes
Order deny,allow
Allow from all

WSGIScriptAlias / /usr/local/lib/python2.6/dist-packages/project_name/apache/django.wsgi

<Directory /usr/local/lib/python2.6/dist-packages/project_name/apache>
Order deny,allow
Allow from all

Activate it:

cd /etc/apache2/sites-enabled/
sudo ln -s ../sites-available/

Configure lighttpd to proxy non-static media requests to Apache

I used MySQL Performance Blog’s “Lighttpd as reverse proxy” article as a basis for my own configuration. Therefore, we’ll have an url, which will require authentication, enabling us to see Apache’s server status.

Create a directory for error logs:

sudo mkdir /var/log/lighttpd/

Create a domain file configuration for lighttpd:

sudo vi /etc/lighttpd/conf-available/

With these contents:

server.modules += ( “mod_auth”,

$HTTP[“host”] =~ “(^|\.)example\.com$” {
$HTTP[“url”] !~ “\.(js|css|gif|jpg|png|ico|txt|swf|html|htm)$” {
proxy.server = ( “” => (
( “host” => “”, “port” => 81 )

server.document-root = “/var/www/”
server.errorlog = “/var/log/lighttpd/”
dir-listing.activate = “disable”

auth.backend = “htpasswd”
auth.backend.htpasswd.userfile = “/var/www/.htpasswd”
auth.require = ( “/server-status” => (
“method” => “basic”,
“realm” => “status”,
“require” => “valid-user”

There are lines worthy of mention in the configuration above:

$HTTP[“host”] =~ “(^|\.)example\.com$” {

This will wrap the directives inside to only apply for requests.

$HTTP[“url”] !~ “\.(js|css|gif|jpg|png|ico|txt|swf|html|htm)$” {
proxy.server = ( “” => (
( “host” => “”, “port” => 81 )

These will send any requests for documents not ending in the specified extensions to ip, port 81, where Apache lives. Essentially, everything which is static content (or more accurately, specified by the | separated regular expression), will be served by lighttpd.

cd /etc/lighttpd/conf-enabled/
sudo ln -s ../conf-available/

Tell the filesystem where you project and Django’s admin static content are located:

sudo ln -s /usr/local/lib/python2.6/dist-packages/django/contrib/admin/media/ /var/www/
sudo ln -s /usr/local/lib/python2.6/dist-packages/project_name/templates/project_media_dir/ /var/www/

Finally, restart everything so the newest configuration can be applied

sudo /etc/init.d/apache2 restart
sudo /etc/init.d/lighttpd restart

So, what actually happens?

When a visitor goes to your website (, the request will hit lighttpd first. If the document path does not end with a string in our list of static content extensions, the request will be proxied to Apache on port 81, otherwise lighttpd will serve itself.

And that’s it, if my memory is correct. Did I miss anything? Comment at will.

[1] Linode, if you’re curious. Mainly because bandwidth is cheaper. If you’re looking for a Linode referral, Linode discount code or Linode promotion code, sign up using this link to credit me as referral. Thanks 🙂


I’m glad Sony back-peddled on this Wipeout ad incarnation

Filed under: Geral — Tags: — inerte @ 01:02

So…. insane

Powered by WordPress