When serving Django applications, the Gunicorn-Nginx combo forms a classic production-grade architecture. Nginx isn't overhead - it's a strategic buffer. Gunicorn excels at running WSGI applications but struggles with:
- Static file handling
- SSL termination
- HTTP/2 support
- Brute force protection
Benchmarks show Nginx reverse proxy configurations handle:
# ApacheBench test results
# Bare Gunicorn:
Requests per second: 342.36 [#/sec]
# Gunicorn + Nginx:
Requests per second: 891.47 [#/sec]
Here's a minimal Nginx config that unlocks major benefits:
server {
listen 80;
server_name example.com;
location /static/ {
alias /path/to/static/files;
expires 30d;
}
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
The reverse proxy setup provides:
- Static file caching: Nginx serves static files 10x faster than Django
- Load balancing: Easily scale to multiple Gunicorn workers
- SSL termination: Offloads encryption overhead from app servers
- Request buffering: Protects against slow client attacks
For high-traffic sites, consider adding:
# In Nginx config
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m inactive=60m;
location / {
proxy_cache my_cache;
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
# Other proxy settings...
}
While Gunicorn is an excellent WSGI server for running Django applications, pairing it with Nginx as a reverse proxy provides significant advantages that go beyond "just another layer." Here's why this combination is widely adopted in production environments:
Nginx excels at handling static files and concurrent connections:
server {
listen 80;
server_name example.com;
location /static/ {
alias /path/to/your/static/files/;
expires 30d;
}
location /media/ {
alias /path/to/your/media/files/;
expires 30d;
}
}
This configuration offloads static file serving from Gunicorn, allowing it to focus on dynamic content.
When scaling your application, Nginx can distribute traffic across multiple Gunicorn workers:
upstream app_server {
server 127.0.0.1:8000;
server 127.0.0.1:8001;
server 127.0.0.1:8002;
}
server {
location / {
proxy_pass http://app_server;
include proxy_params;
}
}
Nginx provides an additional security layer:
- SSL/TLS termination
- Rate limiting
- IP filtering
- Request size limitations
server {
listen 443 ssl;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
client_max_body_size 10M;
location / {
limit_req zone=one burst=10;
proxy_pass http://app_server;
}
}
Nginx's buffering capabilities protect Gunicorn from slow clients:
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 16k;
proxy_busy_buffers_size 24k;
proxy_max_temp_file_size 2048m;
A complete Nginx configuration for Django might look like:
upstream django_app {
server unix:/tmp/gunicorn.sock fail_timeout=0;
}
server {
listen 80;
server_name yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://django_app;
}
location /static/ {
alias /home/django/staticfiles/;
}
location /media/ {
alias /home/django/media/;
}
}
This setup demonstrates how Nginx complements Gunicorn by handling SSL termination, static files, and proper request forwarding.