Nginx powers a huge portion of the web. Understanding its configuration patterns is essential for deploying modern applications.

Basic Structure

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# /etc/nginx/nginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid;

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    
    # Logging
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;
    
    # Performance
    sendfile on;
    keepalive_timeout 65;
    
    # Include site configs
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

Static File Server

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
server {
    listen 80;
    server_name example.com;
    root /var/www/html;
    index index.html;
    
    location / {
        try_files $uri $uri/ =404;
    }
    
    # Cache static assets
    location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
        expires 30d;
        add_header Cache-Control "public, immutable";
    }
}

Reverse Proxy

Basic Proxy

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
server {
    listen 80;
    server_name api.example.com;
    
    location / {
        proxy_pass http://localhost:3000;
        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;
    }
}

WebSocket Support

1
2
3
4
5
6
7
8
location /ws {
    proxy_pass http://localhost:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_read_timeout 86400;
}

Upstream (Multiple Backends)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
upstream backend {
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
    server 127.0.0.1:3003;
    keepalive 32;
}

server {
    location / {
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }
}

Load Balancing

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
upstream backend {
    # Round-robin (default)
    server app1:3000;
    server app2:3000;
    server app3:3000;
}

upstream backend_weighted {
    server app1:3000 weight=3;
    server app2:3000 weight=2;
    server app3:3000 weight=1;
}

upstream backend_ip_hash {
    ip_hash;  # Sticky sessions by IP
    server app1:3000;
    server app2:3000;
}

upstream backend_least_conn {
    least_conn;  # Send to least busy
    server app1:3000;
    server app2:3000;
}

Health Checks

1
2
3
4
5
upstream backend {
    server app1:3000 max_fails=3 fail_timeout=30s;
    server app2:3000 max_fails=3 fail_timeout=30s;
    server app3:3000 backup;  # Only used when others fail
}

SSL/TLS Configuration

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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;
    
    # Modern SSL settings
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;
    
    # HSTS
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    
    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 8.8.8.8 8.8.4.4 valid=300s;
}

# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
}

Security Headers

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
server {
    # Prevent clickjacking
    add_header X-Frame-Options "SAMEORIGIN" always;
    
    # Prevent MIME sniffing
    add_header X-Content-Type-Options "nosniff" always;
    
    # XSS protection
    add_header X-XSS-Protection "1; mode=block" always;
    
    # Referrer policy
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    
    # Content Security Policy
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline';" always;
}

Rate Limiting

1
2
3
4
5
6
7
8
9
# Define rate limit zone
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;

server {
    location /api/ {
        limit_req zone=api burst=20 nodelay;
        proxy_pass http://backend;
    }
}

Connection Limiting

1
2
3
4
5
6
7
8
limit_conn_zone $binary_remote_addr zone=addr:10m;

server {
    location /downloads/ {
        limit_conn addr 5;  # 5 connections per IP
        limit_rate 100k;    # 100KB/s per connection
    }
}

Caching

Proxy Cache

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=cache:10m max_size=1g inactive=60m;

server {
    location / {
        proxy_cache cache;
        proxy_cache_valid 200 1h;
        proxy_cache_valid 404 1m;
        proxy_cache_use_stale error timeout updating;
        proxy_cache_background_update on;
        
        add_header X-Cache-Status $upstream_cache_status;
        
        proxy_pass http://backend;
    }
}

Cache Bypass

1
2
3
4
5
location / {
    proxy_cache cache;
    proxy_cache_bypass $http_cache_control;
    proxy_no_cache $arg_nocache;
}

Gzip Compression

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
http {
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/json
        application/javascript
        application/xml
        application/rss+xml
        image/svg+xml;
}

Location Matching

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
server {
    # Exact match
    location = /favicon.ico {
        log_not_found off;
        access_log off;
    }
    
    # Prefix match (case-sensitive)
    location /api/ {
        proxy_pass http://backend;
    }
    
    # Regex match (case-sensitive)
    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php-fpm.sock;
    }
    
    # Regex match (case-insensitive)
    location ~* \.(jpg|jpeg|png|gif)$ {
        expires 30d;
    }
    
    # Prefix match (highest priority after exact)
    location ^~ /static/ {
        root /var/www;
    }
}

Priority: = > ^~ > ~ / ~* > prefix

Common Patterns

SPA (Single Page Application)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
server {
    root /var/www/app;
    index index.html;
    
    location / {
        try_files $uri $uri/ /index.html;
    }
    
    location /api {
        proxy_pass http://backend;
    }
}

PHP with FastCGI

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
server {
    root /var/www/html;
    index index.php index.html;
    
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
    
    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }
    
    location ~ /\.(?!well-known).* {
        deny all;
    }
}

Subdomain to Path

1
2
3
4
5
6
7
server {
    server_name ~^(?<subdomain>.+)\.example\.com$;
    
    location / {
        proxy_pass http://backend/$subdomain$request_uri;
    }
}

Maintenance Mode

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
server {
    if (-f /var/www/maintenance.flag) {
        return 503;
    }
    
    error_page 503 @maintenance;
    location @maintenance {
        root /var/www/maintenance;
        try_files $uri /index.html =503;
    }
}

Logging

Custom Log Format

1
2
3
4
5
6
7
8
http {
    log_format main '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent" '
                    '$request_time $upstream_response_time';
    
    access_log /var/log/nginx/access.log main;
}

JSON Logging

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
log_format json escape=json '{'
    '"time":"$time_iso8601",'
    '"remote_addr":"$remote_addr",'
    '"method":"$request_method",'
    '"uri":"$request_uri",'
    '"status":$status,'
    '"body_bytes":$body_bytes_sent,'
    '"request_time":$request_time,'
    '"upstream_time":"$upstream_response_time",'
    '"user_agent":"$http_user_agent"'
'}';

Testing Configuration

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Test config syntax
nginx -t

# Test and show parsed config
nginx -T

# Reload without downtime
nginx -s reload

# Or with systemd
systemctl reload nginx

Debugging

1
2
3
4
5
6
7
# Enable debug logging (compile with --with-debug)
error_log /var/log/nginx/error.log debug;

# Debug specific connection
events {
    debug_connection 192.168.1.100;
}

Nginx configuration is declarative and powerful. Master the patterns, and you can serve anything.