Django with Gunicorn
upstream django {
server unix:/run/gunicorn/socket fail_timeout=0;
# Or TCP: server 127.0.0.1:8000;
}
server {
listen 80;
server_name example.com;
return 301 https://example.com$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com;
# SSL
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Logs
access_log /var/log/nginx/django.access.log;
error_log /var/log/nginx/django.error.log;
# Max upload size
client_max_body_size 10M;
# Static files
location /static/ {
alias /var/www/django/staticfiles/;
expires 30d;
access_log off;
}
# Media files
location /media/ {
alias /var/www/django/media/;
expires 30d;
}
# Django app
location / {
proxy_pass http://django;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
# Timeouts
proxy_connect_timeout 60s;
proxy_read_timeout 60s;
}
}
Django with uWSGI
upstream django {
server unix:///var/run/uwsgi/django.sock;
}
server {
listen 443 ssl http2;
server_name example.com;
# Static files
location /static/ {
alias /var/www/django/staticfiles/;
}
# uWSGI
location / {
include uwsgi_params;
uwsgi_pass django;
uwsgi_param Host $host;
uwsgi_param X-Real-IP $remote_addr;
uwsgi_param X-Forwarded-For $proxy_add_x_forwarded_for;
uwsgi_param X-Forwarded-Proto $scheme;
}
}
Django Admin Protection
server {
# ... SSL and basic config ...
# Protect admin area
location /admin/ {
# IP whitelist
allow 192.168.1.0/24;
deny all;
proxy_pass http://django;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location / {
proxy_pass http://django;
# ... headers ...
}
}
Django REST Framework (API)
server {
listen 443 ssl http2;
server_name api.example.com;
# CORS headers
add_header Access-Control-Allow-Origin * always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;
# Handle preflight
location / {
if ($request_method = OPTIONS) {
return 204;
}
proxy_pass http://django;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Gunicorn Systemd Service
# /etc/systemd/system/gunicorn.service
[Unit]
Description=gunicorn daemon
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/django
ExecStart=/var/www/django/venv/bin/gunicorn \
--workers 3 \
--bind unix:/run/gunicorn/socket \
--access-logfile /var/log/gunicorn/access.log \
--error-logfile /var/log/gunicorn/error.log \
myproject.wsgi:application
[Install]
WantedBy=multi-user.target
Django Channels (WebSocket)
upstream channels {
server 127.0.0.1:8001;
}
server {
listen 443 ssl http2;
server_name example.com;
# Regular HTTP
location / {
proxy_pass http://django;
# ... standard headers ...
}
# WebSocket
location /ws/ {
proxy_pass http://channels;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
}
intermediate | Recipes | Updated 2025-01-15
- nginx
- django
- python
- gunicorn
- uwsgi