Overview

Nginx config is organized as a tree of contexts: http contains server blocks, which contain location blocks. Directives inherit from the enclosing context unless overridden. This card covers the directives that make up 90% of real configs: reverse proxy, static file serving, SSL termination, redirects, and compression. Test config with nginx -t before reloading; reload with nginx -s reload (zero downtime) rather than a full restart.

Block structure

The three main blocks and their purpose.

BlockScopeTypical content
http {}Global HTTP settingsinclude mime.types, logging format, gzip, upstream definitions.
server {}One virtual hostlisten, server_name, SSL config, root, location blocks.
location {}URL prefix or regexproxy_pass, try_files, return, caching, headers.
upstream {}Backend poolserver directives listing backends; keepalive.
http {
    include       mime.types;
    default_type  application/octet-stream;
 
    upstream api_backend {
        server 127.0.0.1:3000;
        server 127.0.0.1:3001;
        keepalive 32;
    }
 
    server {
        listen 443 ssl http2;
        server_name example.com;
 
        location /api/ {
            proxy_pass http://api_backend/;
        }
 
        location / {
            root /var/www/html;
            try_files $uri $uri/ /index.html;
        }
    }
}

Location matching

Nginx tests location blocks in a specific order; the first match wins except for exact matches.

ModifierSyntaxPriorityNotes
Exactlocation = /pathHighestMust match the URI exactly; no trailing slash allowed.
Preferential prefixlocation ^~ /pathSecondStops regex search once matched.
Case-sensitive regexlocation ~ \.php$ThirdFirst matching regex wins.
Case-insensitive regexlocation ~* \.jpg$ThirdSame priority as ~; order in file breaks ties.
Prefixlocation /pathLowestLongest prefix match is used if no regex matches.
location = /favicon.ico { access_log off; return 204; }
location ^~ /static/    { root /var/www; expires 1y; }
location ~* \.(jpg|png)$ { expires 30d; add_header Cache-Control "public"; }
location /               { try_files $uri $uri/ =404; }

Common directives

The directives to know for proxying, serving files, and redirects.

DirectiveContextWhat it does
proxy_passlocationForward requests to an upstream URL or pool.
proxy_set_headerlocationSet or forward a header to the upstream.
try_fileslocationTest file paths in order; use last item as fallback or error.
returnserver, locationImmediately return a status or redirect.
rewriteserver, locationRewrite the URI and optionally redirect.
rootserver, locationDocument root; path is prepended to the URI.
aliaslocationReplace the location prefix with the given path.
indexserver, locationDefault file to serve for directory requests.
expireslocationSet Cache-Control and Expires headers.
add_headerserver, locationAdd a response header.
# Reverse proxy with forwarded headers
location /api/ {
    proxy_pass         http://api_backend/;
    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_http_version 1.1;
    proxy_set_header   Connection "";   # enable keepalive to upstream
}
 
# SPA fallback
location / {
    try_files $uri $uri/ /index.html;
}
 
# Permanent redirect
location /old-path {
    return 301 /new-path;
}

SSL configuration

Minimal TLS 1.2/1.3 config for a modern server.

server {
    listen 443 ssl http2;
    server_name example.com;
 
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
 
    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 1d;
    ssl_stapling        on;
    ssl_stapling_verify on;
 
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
}
 
# HTTP to HTTPS redirect
server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

Generate certificates with Certbot: certbot --nginx -d example.com. Certbot edits the nginx config and sets up auto-renewal.

Compression and performance

Enable gzip for text responses; skip binary formats.

http {
    gzip              on;
    gzip_types        text/plain text/css application/json application/javascript
                      text/xml application/xml image/svg+xml;
    gzip_min_length   1024;
    gzip_comp_level   6;      # 1 (fast) to 9 (best compression); 6 is a good balance
    gzip_vary         on;
    gzip_proxied      any;
 
    # Serve pre-compressed .gz files if they exist
    gzip_static on;
 
    # Buffer tuning for proxied responses
    proxy_buffers         16 4k;
    proxy_buffer_size     4k;
    keepalive_timeout     65;
    sendfile              on;
    tcp_nopush            on;
}

Common gotchas

  • proxy_pass http://backend/ (with trailing slash) strips the matched location prefix. proxy_pass http://backend (no trailing slash) passes the full URI. This mismatch causes 404s; pick the form that matches your intent.
  • add_header in a child block replaces, rather than adds to, headers set in a parent block. Set all security headers in one place to avoid this.
  • root prepends the root to the full URI. alias replaces the location prefix. Confusing the two produces 404s. Use alias for locations whose path on disk differs from the URI prefix.
  • try_files with =404 as a fallback returns 404 directly. Using @fallback (a named location) allows custom 404 handling.
  • nginx -t tests syntax but not runtime behavior (e.g., unreachable upstreams). Test proxy connectivity with curl from the nginx host.
  • The regex location ~* \.(gif|jpg)$ must come before broader prefix locations in the file if Nginx must prefer it; the file order matters for same-priority matches.