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.
Description | HandsOnCards | SaltyCrane |
Redirection | http://www.handsoncards.com is redirected to http://handsoncards.com | http://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
- http://bart.whahay.net/blog/2009/04/06/setting-up-nginx-django.html (my main reference)
- http://articles.slicehost.com/2009/3/5/ubuntu-intrepid-nginx-configuration (general nginx configuration)
- http://articles.slicehost.com/2009/3/6/ubuntu-intrepid-nginx-virtual-hosts-1 (nginx virtual hosts configuration)
- http://forum.slicehost.com/comments.php?DiscussionID=2964 (explains the difference between
root
andalias
in nginx.conf.)
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.