Back to Blog
January 15, 20261,000 views

Nginx for Developers: The Config Guide You'll Actually Bookmark

Nginx configs look like hieroglyphics until you understand the mental model. This guide explains what every directive actually does, with copy-paste configs for the 5 most common setups.

The Mental Model: Nginx Is a Mail Room

Think of Nginx as a mail room. Requests arrive at the front desk (port 80/443). The mail room checks the address label (server_name) to route it to the right department (server block). Within the department, it checks the subject line (location) to route it to the right person (upstream/file).

That's it. Everything in Nginx is: match a request β†’ do something with it.

Config 1: Next.js + Django Behind Nginx (Most Common)

# Upstream blocks: define your app servers
upstream frontend {
    server frontend_container:3000;
    keepalive 16;  # reuse connections (huge perf win)
}

upstream backend {
    server backend_container:8000;
    keepalive 8;
}

server {
    listen 443 ssl;
    server_name yourapp.com;

    # SSL (Let's Encrypt)
    ssl_certificate /etc/letsencrypt/live/yourapp.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourapp.com/privkey.pem;

    # API routes -> Django
    location /api/ {
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";  # required for keepalive
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # Admin -> Django
    location /admin/ {
        proxy_pass http://backend;
        proxy_set_header Host $host;
    }

    # Static files -> Django (with caching)
    location /static/ {
        proxy_pass http://backend;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    # Everything else -> Next.js
    location / {
        proxy_pass http://frontend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_buffering off;  # important for streaming SSR
    }
}

Config 2: HTTPS Redirect + www Canonical

Two things every production site needs: force HTTPS and pick one canonical domain (www or non-www). Without this, Google sees two separate sites splitting your SEO.

# Redirect HTTP -> HTTPS
server {
    listen 80;
    server_name yourapp.com www.yourapp.com;
    return 301 https://yourapp.com$request_uri;
}

# Redirect www -> non-www
server {
    listen 443 ssl;
    server_name www.yourapp.com;
    ssl_certificate /etc/letsencrypt/live/yourapp.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourapp.com/privkey.pem;
    return 301 https://yourapp.com$request_uri;
}

# Main site (canonical)
server {
    listen 443 ssl;
    server_name yourapp.com;
    # ... your config
}

The 5 Directives You Must Understand

DirectiveWhat It DoesWhy It Matters
proxy_http_version 1.1Uses HTTP/1.1 for upstream connectionsRequired for keepalive to work
proxy_set_header Connection ""Clears the Connection headerWithout this, keepalive is disabled
proxy_buffering offStreams response directly to clientNeeded for SSR streaming, SSE, real-time
client_max_body_size 20MMax upload sizeDefault is 1MB - file uploads will fail
proxy_set_header X-Forwarded-Proto $schemeTells backend if request was HTTPSDjango needs this for CSRF and redirects

Debugging: When Things Go Wrong

502 Bad Gateway: Nginx can't connect to your upstream. Your app container is not running, or it's on a different Docker network.

504 Gateway Timeout: Your app is too slow. Increase proxy_read_timeout or optimize your app.

413 Request Entity Too Large: Upload exceeds client_max_body_size. Increase it.

301 redirect loop: Your app is redirecting to HTTPS, but X-Forwarded-Proto isn't set, so it always thinks it's HTTP.

# Quick debugging commands
nginx -t                    # test config syntax
nginx -s reload             # reload without downtime
tail -f /var/log/nginx/error.log   # watch errors live
curl -I https://yourapp.com  # check response headers

Nginx is the bouncer at your club. It decides who gets in, where they go, and how fast. Spend 30 minutes understanding it and you'll save 30 hours debugging production issues.

β€” alokknight Engineering