SaltyCrane Blog — Notes on JavaScript and web development

Notes on using nginx with mod_python and Django

Here are my notes on setting up nginx as a reverse proxy with Apache, mod_python, and Django on Ubuntu Intrepid. Nginx is used to serve my static media files while all other requests are passed on to the Apache/mod_python/Django web server.

I realize mod_wsgi has become the preferred way to deploy Django, but I'm a little behind the times and am still using mod_python. I hope to switch to mod_wsgi soon.

I have been using Amazon's CloudFront service for delivering my static media files. As far as I can tell, it has worked well. My main reason for switching to nginx is so I can skip the extra step of uploading to Amazon. Regarding my concern about the memory footprint of nginx, it looks like it is using around 5mb with my two process configuration.

My configuration parameters are shown below. I'm running two sites, SaltyCrane and HandsOnCards on a Slicehost 256mb plan.

DescriptionHandsOnCardsSaltyCrane
Redirectionhttp://www.handsoncards.com is redirected to http://handsoncards.comhttp://saltycrane.com is redirected to http://www.saltycrane.com
Static media filesystem path/srv/HandsOnCards/handsoncards/static//srv/SaltyCrane/iwiwdsmi/media/
Static media web path/site_media//site_media/
Django settings.py file location/srv/HandsOnCards/handsoncards/settings.py/srv/SaltyCrane/iwiwdsmi/settings.py
Additional Python packages path/srv/python-packages/srv/python-packages

Install nginx

Some recommend installing nginx from source, but I took the easier route and used Ubuntu's package manager.

sudo apt-get install nginx

Nginx configuration

Edit /etc/nginx/nginx.conf:

user www-data www-data;
worker_processes  2;

error_log  /var/log/nginx/error.log;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    access_log  /var/log/nginx/access.log;

    sendfile        on;
    tcp_nopush      on;

    keepalive_timeout  3;
    tcp_nodelay        off;

    gzip  on;
    gzip_comp_level 2;
    gzip_proxied any;
    gzip_types      text/plain text/html text/css application/x-javascript text/xml
                    application/xml application/xml+rss text/javascript;

    server {
        listen 80;
        server_name  www.handsoncards.com;
        rewrite ^/(.*) http://handsoncards.com/$1 permanent;
    }
    server {
        listen 80;
        server_name handsoncards.com;
      
        access_log /var/log/nginx/handsoncards.com.access.log;
        error_log /var/log/nginx/handsoncards.com.error.log;
      
        location / {
            proxy_pass http://127.0.0.1:8080/;
            include /etc/nginx/proxy.conf;
        }
        location /site_media/ {
            alias /srv/HandsOnCards/handsoncards/static/;
            expires 24h;
        }
    }
    server {
        listen 80;
        server_name saltycrane.com;
        rewrite ^/(.*) http://www.saltycrane.com/$1 permanent;
    }
    server {
        listen 80;
        server_name www.saltycrane.com;
      
        access_log /var/log/nginx/saltycrane.com.access.log;
        error_log /var/log/nginx/saltycrane.com.error.log;
      
        location / {
            proxy_pass http://127.0.0.1:8080/;
            include /etc/nginx/proxy.conf;
        }
        location /site_media/ {
            alias /srv/SaltyCrane/iwidsmi/media/;
            expires 24h;
        }
    }
}

Edit /etc/nginx/proxy.conf:

proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_buffers 32 4k;

Restart Nginx

sudo /etc/init.d/nginx restart

Install Apache

I already had Apache and mod_python installed, but in case you don't:

apt-get install apache2 apache2-mpm-prefork
apt-get install libapache2-mod-python

Apache Configuration

Edit /etc/apache2/httpd.conf:

MaxClients 2
MaxRequestsPerChild 350
KeepAlive Off
NameVirtualHost 127.0.0.1:8080
Listen 8080

<VirtualHost 127.0.0.1:8080>
    ServerName www.saltycrane.com
    <Location "/">
        SetHandler python-program
        PythonHandler django.core.handlers.modpython
        SetEnv DJANGO_SETTINGS_MODULE iwiwdsmi.settings
        PythonPath "['/srv/SaltyCrane', '/srv/python-packages'] + sys.path"
        PythonDebug Off
    </Location>
</VirtualHost>

<VirtualHost 127.0.0.1:8080>
    ServerName handsoncards.com
    <Location "/">
        SetHandler python-program
        PythonHandler django.core.handlers.modpython
        SetEnv DJANGO_SETTINGS_MODULE handsoncards.settings
        PythonPath "['/srv/HandsOnCards', '/srv/python-packages'] + sys.path"
        PythonDebug Off
    </Location>
</VirtualHost>

Edit /etc/apache2/ports.conf and comment out the following two lines:

#NameVirtualHost *:80
#Listen 80

Restart Apache

sudo /etc/init.d/apache2 restart

Add Django's reverse proxy middleware

Edit your settings.py file to include django.middleware.http.SetRemoteAddrFromForwardedFor in MIDDLEWARE_CLASSES. This allows your Django application to use the real IP address of the client instead of 127.0.0.1 from your nginx proxy. It sets Django's request.META['REMOTE_ADDR'] to be request.META['HTTP_X_FORWARDED_FOR'] which we set above in nginx's proxy.conf. For more information, see the Middleware reference in the Django documentation.

MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.middleware.http.SetRemoteAddrFromForwardedFor',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
)

References

Errors

  • A 502 Bad Gateway from Nginx probably means there is something wrong with your Apache setup
  • An Internal Server Error from Apache means something is probably wrong with your Django setup.

Comments


#1 Mateu Mir commented on :

Good!!!

You saved my life!!