Skip to main content

Django with Nginx

Introduction

When you're ready to deploy your Django application to a production environment, you'll need a robust web server to handle client requests efficiently. While Django comes with a built-in development server, it's not suitable for production use due to security and performance limitations. This is where Nginx comes in.

Nginx (pronounced "engine-x") is a powerful, open-source web server that can serve as a reverse proxy, load balancer, and HTTP cache for your Django applications. In this tutorial, we'll explore how to configure Nginx to work with Django and understand why this combination is the preferred setup for production environments.

Why Use Nginx with Django?

Before diving into configuration, let's understand why using Nginx with Django is beneficial:

  1. Performance: Nginx is designed to handle many concurrent connections efficiently
  2. Static Files: Nginx excels at serving static files (images, CSS, JavaScript)
  3. Security: Adds an additional layer of protection for your application
  4. SSL Termination: Handles HTTPS connections before passing requests to Django
  5. Load Balancing: Can distribute traffic across multiple Django instances

Architecture Overview

In a Django-Nginx setup, the typical architecture looks like this:

Client Request → Nginx → WSGI Server (Gunicorn/uWSGI) → Django Application

Nginx serves as the front-facing web server that handles all incoming HTTP(S) requests. It then forwards appropriate requests to a WSGI server like Gunicorn, which runs your Django application.

Prerequisites

Before configuring Nginx with Django, ensure you have:

  • A Django project ready for deployment
  • A server with Ubuntu/Debian (though instructions can be adapted for other systems)
  • SSH access to your server
  • Basic understanding of command line operations
  • Domain name pointed to your server (optional but recommended)

Step 1: Install Nginx

First, let's install Nginx on your server:

bash
# Update package lists
sudo apt update

# Install Nginx
sudo apt install nginx

# Start Nginx service
sudo systemctl start nginx

# Enable Nginx to start at boot
sudo systemctl enable nginx

To check if Nginx is running properly:

bash
sudo systemctl status nginx

You should see output indicating that Nginx is active and running.

Step 2: Set Up a Django Project with Gunicorn

Before configuring Nginx, we need to set up our Django project with a WSGI server like Gunicorn. If you haven't installed Gunicorn yet:

bash
pip install gunicorn

Add Gunicorn to your project's requirements:

bash
echo "gunicorn" >> requirements.txt

Test if Gunicorn can serve your Django application:

bash
cd /path/to/your/django/project
gunicorn --bind 0.0.0.0:8000 myproject.wsgi:application

If everything is working correctly, you should be able to access your Django application at http://your-server-ip:8000.

Step 3: Configure Nginx for Django

Now, let's configure Nginx to work with our Django application. Create a new Nginx server block configuration:

bash
sudo nano /etc/nginx/sites-available/myproject

Add the following configuration (replace placeholders with your specific details):

nginx
server {
listen 80;
server_name example.com www.example.com; # Replace with your domain or server IP

location = /favicon.ico {
access_log off;
log_not_found off;
}

location /static/ {
root /path/to/your/django/project; # Path to your Django project's static files
}

location /media/ {
root /path/to/your/django/project; # Path to your Django project's media files
}

location / {
include proxy_params;
proxy_pass http://127.0.0.1:8000; # Forward to Gunicorn
}
}

This configuration:

  • Listens for HTTP requests on port 80
  • Serves static and media files directly (more efficient than Django)
  • Forwards all other requests to Gunicorn running on port 8000

Next, create a symbolic link to enable this configuration:

bash
sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled

Test the Nginx configuration for syntax errors:

bash
sudo nginx -t

If the test is successful, reload Nginx to apply the changes:

bash
sudo systemctl reload nginx

Step 4: Setting Up Gunicorn as a System Service

To ensure Gunicorn runs automatically and restarts if it crashes, let's create a systemd service file:

bash
sudo nano /etc/systemd/system/gunicorn.service

Add the following content:

ini
[Unit]
Description=gunicorn daemon
After=network.target

[Service]
User=your_user # Replace with your system username
Group=www-data
WorkingDirectory=/path/to/your/django/project
ExecStart=/path/to/your/virtualenv/bin/gunicorn \
--access-logfile - \
--workers 3 \
--bind 127.0.0.1:8000 \
myproject.wsgi:application

[Install]
WantedBy=multi-user.target

Start and enable the Gunicorn service:

bash
sudo systemctl start gunicorn
sudo systemctl enable gunicorn

Check the status to ensure it's running correctly:

bash
sudo systemctl status gunicorn

For a production site, you should enable HTTPS. Let's install Certbot to get free SSL certificates from Let's Encrypt:

bash
sudo apt install certbot python3-certbot-nginx

Obtain and install SSL certificates:

bash
sudo certbot --nginx -d example.com -d www.example.com

Follow the prompts to complete the certificate setup. Certbot will automatically modify your Nginx configuration to use the SSL certificates.

Step 6: Optimizing Nginx for Django

Here are some additional optimizations you can add to your Nginx configuration:

nginx
server {
# ... existing configuration ...

# Add gzip compression
gzip on;
gzip_vary on;
gzip_min_length 10240;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml;
gzip_disable "MSIE [1-6]\.";

# Cache static files
location /static/ {
root /path/to/your/django/project;
expires 30d;
add_header Cache-Control "public, max-age=2592000";
}

# Increase client body size if you need to handle large file uploads
client_max_body_size 10M;
}

After making these changes, reload Nginx:

bash
sudo systemctl reload nginx

Real-world Example: Complete Configuration

Here's a comprehensive example of a production-ready Nginx configuration for a Django application:

nginx
server {
listen 80;
server_name mydjangoapp.com www.mydjangoapp.com;
return 301 https://$host$request_uri; # Redirect all HTTP to HTTPS
}

server {
listen 443 ssl;
server_name mydjangoapp.com www.mydjangoapp.com;

ssl_certificate /etc/letsencrypt/live/mydjangoapp.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mydjangoapp.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers EECDH+AESGCM:EDH+AESGCM;
ssl_session_cache shared:SSL:10m;

access_log /var/log/nginx/mydjangoapp.access.log;
error_log /var/log/nginx/mydjangoapp.error.log;

# Static files
location /static/ {
alias /var/www/mydjangoapp/static/;
expires 30d;
add_header Cache-Control "public, max-age=2592000";
}

# Media files
location /media/ {
alias /var/www/mydjangoapp/media/;
expires 30d;
add_header Cache-Control "public, max-age=2592000";
}

# Forward to Django/Gunicorn
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;
proxy_set_header X-Forwarded-Proto $scheme;
}

# Security headers
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header X-Frame-Options SAMEORIGIN;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

# Deny access to .htaccess files
location ~ /\.ht {
deny all;
}
}

Troubleshooting Common Issues

1. 502 Bad Gateway

If you see a "502 Bad Gateway" error, it usually means Nginx can't connect to Gunicorn:

  • Check if Gunicorn is running: sudo systemctl status gunicorn
  • Verify the socket or port in both Nginx configuration and Gunicorn service
  • Check Gunicorn error logs: journalctl -u gunicorn
  • Ensure permissions are correct in your project directories

2. Static Files Not Loading

If static files aren't loading properly:

  • Verify the path in your Nginx configuration
  • Ensure you've run python manage.py collectstatic
  • Check directory permissions
  • Verify the URL patterns in your Django settings (STATIC_URL, STATIC_ROOT)

3. Permission Denied Errors

If you see permission errors in Nginx logs:

bash
sudo chown -R www-data:www-data /path/to/your/django/project/static
sudo chown -R www-data:www-data /path/to/your/django/project/media

Summary

In this tutorial, we've covered:

  1. Why Nginx is essential for a production Django setup
  2. How to install and configure Nginx
  3. Setting up Gunicorn as a system service
  4. Configuring Nginx to work with Django
  5. Adding SSL for secure connections
  6. Optimizing Nginx for better performance
  7. Troubleshooting common issues

By following these steps, you've created a robust, production-ready Django deployment with Nginx handling client requests efficiently. This setup provides better security, performance, and reliability than using Django's development server alone.

Additional Resources

Exercises

  1. Configure Nginx to serve multiple Django applications on the same server using different domain names.
  2. Set up rate limiting in your Nginx configuration to prevent abuse.
  3. Configure Nginx to serve a maintenance page when your Django application is down for maintenance.
  4. Implement HTTP/2 in your Nginx configuration for improved performance.
  5. Set up Nginx to cache responses from your Django application to reduce load on your application server.


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)