---
# === IDENTITY ===
id: software/devops/nginx-common-configs/2026
canonical_question: "nginx.conf reference for common scenarios"
aliases:
  - "nginx reverse proxy configuration"
  - "nginx SSL TLS HTTPS setup"
  - "nginx.conf static files and proxy_pass"
  - "nginx configuration best practices"
entity_type: software_reference
domain: software > devops > nginx Common Configs
region: global
jurisdiction: global
temporal_scope: 2024-2026

# === VERIFICATION ===
last_verified: 2026-02-28
confidence: 0.94
version: 1.0
first_published: 2026-02-28

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: stable
  last_breaking_change: "nginx 1.25 — HTTP/3 QUIC support GA"
  next_review: 2026-08-27
  change_sensitivity: low

# === CONSTRAINTS ===
constraints:
  - "Always test config before reload: nginx -t must pass before nginx -s reload"
  - "Never expose server version: set server_tokens off in http block"
  - "SSL: minimum TLS 1.2 — disable SSLv3, TLS 1.0, TLS 1.1"
  - "Never use if for request routing — use map or separate server blocks instead"
  - "worker_connections * worker_processes must not exceed OS ulimit -n"

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "You want automatic HTTPS with minimal config"
    use_instead: "software/devops/caddy-server-config/2026"
  - condition: "You need a serverless edge proxy"
    use_instead: "software/devops/cloudflare-workers-setup/2026"

# === AGENT HINTS ===
inputs_needed:
  - key: "use_case"
    question: "What is the primary use case for nginx?"
    type: choice
    options: ["Reverse proxy", "Static file server", "Load balancer", "SSL termination", "All-in-one"]
  - key: "ssl_provider"
    question: "How are you handling SSL certificates?"
    type: choice
    options: ["Let's Encrypt (certbot)", "Self-signed", "Managed (AWS ACM, Cloudflare)", "None (HTTP only)"]

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/software/devops/nginx-common-configs/2026"
suggested_citation: "Source: knowledgelib.io — AI Knowledge Library (verified 2026-02-28)"

# === RELATED UNITS ===
related_kos:
  depends_on: []
  related_to: []
  solves: []
  alternative_to:
    - id: software/devops/caddy-server-config/2026
      label: "Caddy Server Configuration Reference"
  often_confused_with: []

# === SOURCES ===
sources:
  - id: src1
    title: "NGINX Reverse Proxy Documentation"
    author: NGINX
    url: https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy
    type: official_docs
    published: 2025-01-01
    reliability: authoritative
  - id: src2
    title: "Configuring HTTPS Servers"
    author: nginx.org
    url: https://nginx.org/en/docs/http/configuring_https_servers.html
    type: official_docs
    published: 2025-01-01
    reliability: authoritative
  - id: src3
    title: "Pitfalls and Common Mistakes"
    author: NGINX Wiki
    url: https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/
    type: official_docs
    published: 2024-06-01
    reliability: authoritative
  - id: src4
    title: "nginx Common Issues and Misconfigurations"
    author: Gorbe
    url: https://gorbe.io/posts/nginx/common-issues-and-misconfigurations/
    type: technical_blog
    published: 2024-11-01
    reliability: moderate_high
  - id: src5
    title: "NGINX Security Hardening Guide"
    author: SecOps Solution
    url: https://www.secopsolution.com/blog/nginx-security-hardening-guide
    type: technical_blog
    published: 2024-09-01
    reliability: moderate_high
  - id: src6
    title: "nginx-admins-handbook"
    author: trimstray
    url: https://github.com/trimstray/nginx-admins-handbook
    type: community_resource
    published: 2024-06-01
    reliability: moderate_high
  - id: src7
    title: "Top 25 Nginx Security Best Practices"
    author: nixCraft
    url: https://www.cyberciti.biz/tips/linux-unix-bsd-nginx-webserver-security.html
    type: community_resource
    published: 2024-12-01
    reliability: moderate_high
---

# nginx.conf Reference for Common Scenarios

## TL;DR

- **Bottom line**: nginx is the most deployed web server/reverse proxy — these config patterns cover 90% of use cases: reverse proxy, static files, SSL termination, load balancing, and rate limiting.
- **Key tool/command**: `nginx -t && nginx -s reload`
- **Watch out for**: Never use `if` for routing — it causes surprising behavior in location blocks. Use `map` directives or separate `server` blocks instead.
- **Works with**: nginx 1.24+ (mainline 1.27+), OpenSSL 3.x, Let's Encrypt / certbot, all Linux distros.

## Constraints
<!-- Agents: read this section before recommending any action from this unit.
     These are hard constraints that must not be violated. -->

- Always run `nginx -t` before `nginx -s reload` — a bad config can take down the server.
- Set `server_tokens off` — never expose nginx version in headers or error pages.
- SSL minimum: TLS 1.2. Disable SSLv3, TLS 1.0, TLS 1.1.
- Never use `if` inside `location` blocks for routing — it is not a general-purpose conditional, and causes counter-intuitive behavior.
- `worker_connections * worker_processes` must not exceed `ulimit -n` (file descriptor limit).
- Keep `proxy_buffering on` (default) — turning it off synchronously blocks worker processes.

## Quick Reference

| Directive | Context | Default | Purpose |
|---|---|---|---|
| `worker_processes` | main | `auto` | Set to CPU core count |
| `worker_connections` | events | `512` | Max connections per worker |
| `server_tokens` | http | `on` | Set to `off` to hide version |
| `client_max_body_size` | http/server | `1m` | Max upload size |
| `proxy_pass` | location | — | Backend URL for reverse proxy |
| `proxy_set_header Host` | location | `$proxy_host` | Set to `$host` to pass original |
| `proxy_set_header X-Real-IP` | location | — | Pass client IP to backend |
| `ssl_protocols` | http/server | `TLSv1.2 TLSv1.3` | Minimum TLS versions |
| `ssl_certificate` | server | — | Path to fullchain.pem |
| `ssl_session_cache` | http | `none` | Set to `shared:SSL:10m` |
| `gzip` | http | `off` | Enable compression |
| `add_header X-Frame-Options` | http/server | — | `DENY` or `SAMEORIGIN` |
| `limit_req_zone` | http | — | Rate limiting definition |
| `upstream` | http | — | Load balancer backend pool |

## Decision Tree

```
START
├── Serving static files only?
│   ├── YES → Static file server config (Example 1)
│   └── NO ↓
├── Proxying to one backend (Node, Python, etc.)?
│   ├── YES → Single reverse proxy (Step-by-Step Guide)
│   └── NO ↓
├── Proxying to multiple backends?
│   ├── YES → Upstream load balancer (Example 2)
│   └── NO ↓
├── Need SSL termination?
│   ├── YES → Add SSL block with Let's Encrypt (Step 3)
│   └── NO ↓
├── Need rate limiting?
│   ├── YES → Add limit_req_zone (Step 5)
│   └── NO ↓
└── DEFAULT → Reverse proxy + SSL + security headers
```

## Step-by-Step Guide

### 1. Install and verify nginx

Install nginx and check the default config. [src6]

```bash
# Ubuntu/Debian
sudo apt update && sudo apt install -y nginx

# CentOS/RHEL
sudo dnf install -y nginx

# macOS
brew install nginx
```

**Verify**: `nginx -v` → `nginx version: nginx/1.24.0` (or higher)

### 2. Configure a reverse proxy

Route traffic to a backend application. [src1]

```nginx
# /etc/nginx/sites-available/myapp.conf

server {
    listen 80;
    server_name example.com www.example.com;

    # Security headers
    server_tokens off;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    location / {
        proxy_pass http://127.0.0.1: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
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Timeouts
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }

    # Static assets — serve directly, bypass proxy
    location /static/ {
        alias /var/www/myapp/static/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }
}
```

**Verify**: `sudo nginx -t` → `syntax is ok`, then `sudo nginx -s reload`

### 3. Add SSL with Let's Encrypt

Enable HTTPS with automatic certificate management. [src2]

```bash
# Install certbot
sudo apt install -y certbot python3-certbot-nginx

# Obtain certificate (modifies nginx config automatically)
sudo certbot --nginx -d example.com -d www.example.com

# Auto-renewal is set up by certbot; verify with:
sudo certbot renew --dry-run
```

Or configure SSL manually:

```nginx
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 10m;
    ssl_session_tickets off;

    # HSTS
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

    location / {
        proxy_pass http://127.0.0.1:3000;
        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;
    }
}

# HTTP to HTTPS redirect
server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri;
}
```

**Verify**: `curl -I https://example.com` → `HTTP/2 200`, check headers for HSTS

### 4. Configure load balancing

Distribute traffic across multiple backends. [src1]

```nginx
upstream backend {
    least_conn;  # Options: round-robin (default), least_conn, ip_hash

    server 127.0.0.1:3001 weight=3;
    server 127.0.0.1:3002 weight=2;
    server 127.0.0.1:3003 backup;

    keepalive 32;  # Persistent connections to backend
}

server {
    listen 443 ssl http2;
    server_name api.example.com;

    # SSL config (as above)...

    location / {
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
```

**Verify**: `sudo nginx -t && sudo nginx -s reload`, then check backend distribution with access logs

### 5. Add rate limiting

Protect against brute-force and DDoS. [src5] [src7]

```nginx
# In http block (/etc/nginx/nginx.conf)
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;

server {
    # ...

    location /api/ {
        limit_req zone=api burst=20 nodelay;
        proxy_pass http://backend;
    }

    location /login {
        limit_req zone=login burst=3 nodelay;
        limit_req_status 429;
        proxy_pass http://backend;
    }
}
```

**Verify**: `ab -n 100 -c 10 https://example.com/api/` → check for 429 responses after burst

### 6. Enable gzip compression

Reduce bandwidth for text-based responses. [src6]

```nginx
# In http block
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 4;
gzip_min_length 256;
gzip_types
    text/plain
    text/css
    text/javascript
    application/javascript
    application/json
    application/xml
    application/xml+rss
    image/svg+xml;
```

**Verify**: `curl -H "Accept-Encoding: gzip" -I https://example.com` → `Content-Encoding: gzip`

## Code Examples

### Static file server with caching

> Full script: [static-file-server-with-caching.txt](scripts/static-file-server-with-caching.txt) (31 lines)

```nginx
# Input:  Requests for static assets
# Output: Files served with aggressive caching headers
server {
    listen 80;
    server_name static.example.com;
# ... (see full script)
```

### Full production config: reverse proxy + SSL + security

> Full script: [full-production-config-reverse-proxy-ssl-security.txt](scripts/full-production-config-reverse-proxy-ssl-security.txt) (43 lines)

```nginx
# Input:  Production nginx.conf
# Output: Secure reverse proxy with all hardening
user nginx;
worker_processes auto;
worker_rlimit_nofile 65535;
# ... (see full script)
```

## Anti-Patterns

### Wrong: Using `if` for routing inside location

```nginx
# BAD — "if is evil" in nginx
location / {
    if ($request_uri ~* "/api") {
        proxy_pass http://api_backend;  # May not work as expected
    }
    if ($request_uri ~* "/web") {
        proxy_pass http://web_backend;  # Unpredictable behavior
    }
}
```

### Correct: Use separate location blocks

```nginx
# GOOD — explicit location matching
location /api/ {
    proxy_pass http://api_backend;
}

location / {
    proxy_pass http://web_backend;
}
```

### Wrong: Missing Host header in proxy_pass

```nginx
# BAD — backend sees proxy_host instead of original domain
location / {
    proxy_pass http://127.0.0.1:3000;
    # Missing proxy_set_header Host
}
```

### Correct: Forward all relevant headers

```nginx
# GOOD — backend sees original request context
location / {
    proxy_pass http://127.0.0.1:3000;
    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;
}
```

### Wrong: Serving with server_tokens on and no security headers

```nginx
# BAD — version exposed, no XSS/clickjacking protection
server {
    listen 80;
    server_name example.com;
    # server_tokens defaults to on
    # No security headers
}
```

### Correct: Hardened security headers

```nginx
# GOOD — version hidden, security headers set
server {
    listen 443 ssl http2;
    server_name example.com;
    server_tokens off;

    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Strict-Transport-Security "max-age=63072000" always;
    add_header Content-Security-Policy "default-src 'self'" always;
}
```

## Common Pitfalls

- **Forgetting `nginx -t` before reload**: A syntax error in config will fail the reload silently or crash the server. Fix: always run `nginx -t && nginx -s reload`. [src3]
- **`proxy_pass` with trailing slash mismatch**: `proxy_pass http://backend` and `proxy_pass http://backend/` behave differently with location path. Fix: be consistent — use trailing slash on both location and proxy_pass, or neither. [src3]
- **Too low `client_max_body_size`**: Default 1 MB rejects file uploads. Fix: set `client_max_body_size 10m;` (or higher) in server block. [src4]
- **Not setting `worker_rlimit_nofile`**: Under load, nginx hits file descriptor limits. Fix: set `worker_rlimit_nofile 65535;` and match OS `ulimit -n`. [src4]
- **Disabling `proxy_buffering`**: Causes worker processes to block on slow clients. Fix: keep `proxy_buffering on` (default) and tune buffer sizes. [src4]
- **Using SSLv3 or TLS 1.0/1.1**: Vulnerable to POODLE and BEAST attacks. Fix: `ssl_protocols TLSv1.2 TLSv1.3;` only. [src5]
- **Exposing `/nginx_status` publicly**: Leaks connection counts and server info. Fix: restrict to `allow 127.0.0.1; deny all;`. [src7]

## Diagnostic Commands

```bash
# Test configuration syntax
sudo nginx -t

# Reload configuration (zero-downtime)
sudo nginx -s reload

# Show compiled-in modules
nginx -V

# Check running nginx processes
ps aux | grep nginx

# View access logs in real-time
tail -f /var/log/nginx/access.log

# View error logs
tail -f /var/log/nginx/error.log

# Check open connections
ss -tlnp | grep nginx

# Test SSL configuration
openssl s_client -connect example.com:443 -tls1_2

# Check certificate expiry
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates
```

## Version History & Compatibility

| Version | Status | Breaking Changes | Migration Notes |
|---|---|---|---|
| nginx 1.27.x | Mainline | HTTP/3 QUIC improvements | Use for latest features |
| nginx 1.26.x | Stable | None | Recommended for production |
| nginx 1.24.x | Previous stable | — | Still widely deployed |
| nginx 1.22.x | EOL | — | Upgrade to 1.26+ |
| OpenSSL 3.x | Current | Deprecated algorithms | Required for TLS 1.3 |

## When to Use / When Not to Use

| Use When | Don't Use When | Use Instead |
|---|---|---|
| Reverse proxy for any backend (Node, Python, Go) | Need automatic HTTPS with zero config | Caddy |
| High-performance static file serving | Simple development proxy | webpack-dev-server, Vite |
| Load balancing across multiple backends | Service mesh with complex routing | Envoy, Istio |
| SSL termination at the edge | Edge computing with code execution | Cloudflare Workers |
| Rate limiting and basic WAF | Full WAF with OWASP rules | ModSecurity, Cloudflare WAF |

## Important Caveats

- The `if` directive in nginx is not a general-purpose conditional — it only works reliably in `server` context for `return` and `rewrite`, not inside `location` blocks with `proxy_pass`.
- `proxy_pass` URL handling changes based on whether a URI is included: `proxy_pass http://backend` preserves the original URI; `proxy_pass http://backend/` replaces the matched location prefix.
- nginx does NOT automatically reload when config files change — you must explicitly send `nginx -s reload` or `kill -HUP`.
- HTTP/3 (QUIC) requires nginx compiled with `--with-http_v3_module` and is only available in mainline (1.25+), not stable branches below 1.26.

## Related Units

- [Caddy Server Configuration Reference](/software/devops/caddy-server-config/2026)
