# nginx Configuration — server blocks, reverse proxy and TLS

> Practical nginx.conf reference — server and location blocks, reverse proxy, TLS, gzip, caching, rate limiting and security headers with examples.

Source: https://www.jpkc.com/db/en/cheatsheets/web-servers/nginx-conf/

<!-- PROSE:intro -->
The nginx.conf is the heart of every nginx server: through directives and nested blocks you define how requests are accepted, routed and served. This reference collects the snippets you reach for daily — from server and location blocks through reverse proxy and TLS to gzip, caching, rate limiting and security headers. Depending on the directive, the examples belong in the http, server or location context of your `/etc/nginx/nginx.conf`, or in the site files under `sites-available/`. After every change, validate the config with `nginx -t` and apply it with a reload — that way a typo never takes your site down. To control the service itself, see the separate nginx CLI cheat sheet.
<!-- PROSE:intro:end -->

## Config Structure & Contexts

Top-level nginx.conf structure. The http block wraps all web config. include pulls in site files.

```bash
# nginx.conf top-level structure
worker_processes auto;
events {
    worker_connections 1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}
```

Basic server block (virtual host). Defines which port and domain this block handles.

```bash
server {
    listen 80;
    server_name example.com www.example.com;
    root /var/www/html;
    index index.php index.html;
}
```

Location blocks match request URIs. Exact (=), prefix (/), case-insensitive regex (~*), and case-sensitive regex (~).

```bash
location / {
    try_files $uri $uri/ =404;
}
location /api/ {
    proxy_pass http://localhost:3000/;
}
location ~* \.php$ {
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
```

NGINX evaluates location blocks in this priority order. Exact matches (=) always win.

```bash
# Location match priority (highest to lowest):
# 1. = exact match
# 2. ^~ prefix match (stops regex search)
# 3. ~  case-sensitive regex
# 4. ~* case-insensitive regex
# 5. /  prefix match (longest wins)
```

## Virtual Hosts (HTTP)

Complete basic HTTP virtual host with document root, index files, and separate log files.

```bash
server {
    listen 80;
    server_name example.com www.example.com;
    root /var/www/example;
    index index.php index.html index.htm;
    access_log /var/log/nginx/example-access.log;
    error_log  /var/log/nginx/example-error.log;
    location / {
        try_files $uri $uri/ =404;
    }
}
```

Default catch-all server block. Drops requests with no matching server_name (return 444 closes connection).

```bash
server {
    listen 80 default_server;
    server_name _;
    return 444;
}
```

Redirect www to non-www (or vice versa) with a permanent 301 redirect.

```bash
server {
    listen 80;
    server_name www.example.com;
    return 301 https://example.com$request_uri;
}
```

## SSL / TLS (HTTPS)

HTTPS server block with Let's Encrypt certificate. TLS 1.2 and 1.3 only, weak ciphers disabled.

```bash
server {
    listen 443 ssl;
    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         HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;
}
```

Redirect all HTTP traffic to HTTPS. Place above the SSL server block.

```bash
server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}
```

SSL performance and security options. Session cache reduces handshake overhead. OCSP stapling speeds up certificate validation.

```bash
ssl_session_cache   shared:SSL:10m;
ssl_session_timeout 1d;
ssl_dhparam /etc/nginx/dhparam.pem;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 1.1.1.1 valid=300s;
```

`listen 443 ssl http2;` — Enable HTTP/2 alongside SSL. HTTP/2 requires HTTPS and significantly improves performance. Since nginx 1.25.1, the separate `http2 on;` directive is recommended.

```bash
listen 443 ssl http2;
```

`add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;` — HSTS header: instructs browsers to always use HTTPS for this domain for 1 year.

```bash
add_header Strict-Transport-Security "max-age=31536000" always;
```

## Reverse Proxy

Reverse proxy: forward /api/ requests to a backend app on port 3000. Passes client IP and protocol headers.

```bash
location /api/ {
    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;
}
```

Required headers for WebSocket proxying. Add inside the proxy location block.

```bash
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
```

Proxy timeout and buffering settings. Increase timeouts for slow backend applications.

```bash
proxy_connect_timeout 60s;
proxy_send_timeout    60s;
proxy_read_timeout    60s;
proxy_buffering       on;
proxy_buffer_size     16k;
proxy_buffers         4 16k;
```

Enable proxy response caching. Cached responses serve directly from disk, reducing backend load.

```bash
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=1g inactive=60m;
location / {
    proxy_cache my_cache;
    proxy_cache_valid 200 302 10m;
    proxy_cache_valid 404 1m;
    proxy_pass http://backend;
}
```

## PHP-FPM Integration

Pass PHP requests to PHP-FPM via Unix socket. Adjust socket path for your PHP version.

```bash
location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
```

Pass PHP requests to PHP-FPM via TCP on port 9000. Use TCP when FPM runs on a different host.

```bash
location ~ \.php$ {
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include        fastcgi_params;
}
```

Front controller pattern: route all non-file requests to index.php (required for Laravel, WordPress, etc.).

```bash
location / {
    try_files $uri $uri/ /index.php?$query_string;
}
```

Cache FastCGI (PHP-FPM) responses to reduce PHP execution on repeated requests.

```bash
fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2 keys_zone=php_cache:10m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
location ~ \.php$ {
    fastcgi_cache php_cache;
    fastcgi_cache_valid 200 5m;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
```

## Rewrites & Redirects

`return 301 https://example.com$request_uri;` — Permanent redirect to a new URL. Faster than rewrite: no regex processing.

```bash
return 301 https://example.com$request_uri;
```

`return 302 /maintenance.html;` — Temporary redirect to a maintenance page.

```bash
return 302 /maintenance.html;
```

Rewrite rules: 'permanent' = 301, 'redirect' = 302. Use return when possible; rewrite for pattern matching.

```bash
rewrite ^/old-page$ /new-page permanent;
rewrite ^/blog/(.*)$ /posts/$1 redirect;
```

`rewrite ^/index\.php/?(.*)$ /$1 permanent;` — Remove index.php from all URLs with a permanent redirect.

```bash
rewrite ^/index\.php/?(.*)$ /$1 permanent;
```

Strip www from any domain using a regex capture group. Prefer a separate server block for this.

```bash
if ($host ~* ^www\.(.+)$) {
    return 301 https://$1$request_uri;
}
```

Custom error pages. The location block serves the error page from a specific path.

```bash
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
    root /var/www/html;
}
```

## Gzip Compression

Enable gzip compression for common text-based content types. Place inside the http block.

```bash
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 1024;
gzip_types text/plain text/css text/javascript application/javascript application/json application/xml image/svg+xml;
```

`gzip_disable "msie6";` — Disable gzip for Internet Explorer 6 (buggy gzip implementation). Safe to include.

```bash
gzip_disable "msie6";
```

`gunzip on;` — Decompress gzip responses from upstream before passing to clients that don't support gzip.

```bash
gunzip on;
```

## Static Files & Browser Caching

Cache images for 30 days in the browser. 'immutable' tells the browser not to revalidate.

```bash
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp)$ {
    expires 30d;
    add_header Cache-Control "public, immutable";
}
```

Cache CSS and JS for 1 year. Works best with versioned/hashed filenames.

```bash
location ~* \.(css|js)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}
```

Long-term caching for web fonts. CORS header required when fonts are loaded cross-origin.

```bash
location ~* \.(woff|woff2|ttf|eot)$ {
    expires 1y;
    add_header Cache-Control "public";
    add_header Access-Control-Allow-Origin "*";
}
```

Suppress 404 log entries for favicon.ico and robots.txt (very common requests).

```bash
location = /favicon.ico {
    log_not_found off;
    access_log off;
}
location = /robots.txt {
    allow all;
    log_not_found off;
    access_log off;
}
```

Performance basics for the http block. sendfile enables kernel-level file transfer. keepalive reduces connection overhead.

```bash
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
```

## Security Headers

Essential security headers. 'always' ensures the header is added even on error responses.

```bash
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;
```

`add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;" always;` — Content Security Policy header. Restricts what resources the browser can load. Adjust sources to fit your app.

```bash
add_header Content-Security-Policy "default-src 'self';" always;
```

`add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;` — Permissions Policy: disable camera, microphone, and geolocation APIs for this site.

```bash
add_header Permissions-Policy "camera=(), microphone=()" always;
```

`server_tokens off;` — Hide the NGINX version number from HTTP response headers and error pages.

```bash
server_tokens off;
```

Block access to hidden files and directories (e.g. .git, .env, .htaccess). Allows .well-known for ACME challenges.

```bash
location ~ /\.(?!well-known) {
    deny all;
}
```

Block direct access to sensitive file types (backups, configs, logs, etc.).

```bash
location ~* \.(bak|conf|dist|fla|inc|ini|log|psd|sh|sql|swp)$ {
    deny all;
}
```

## Rate Limiting & Access Control

Rate limiting: allow max 10 requests/second per IP, bursting up to 20. Excess requests get 503.

```bash
# In http block:
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
# In server or location block:
limit_req zone=req_limit burst=20 nodelay;
```

Limit the number of concurrent connections per IP to 10.

```bash
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
limit_conn conn_limit 10;
```

Whitelist specific IPs or subnets and deny all others. Useful for admin areas.

```bash
allow 192.168.1.0/24;
allow 10.0.0.1;
deny all;
```

HTTP Basic Authentication. Generate .htpasswd with: htpasswd -c /etc/nginx/.htpasswd username

```bash
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/.htpasswd;
```

`client_max_body_size 50M;` — Set the maximum allowed size of the client request body. Increase for file uploads.

```bash
client_max_body_size 50M;
```

## Load Balancing

Round-robin load balancing across three backend servers. Requests are distributed evenly.

```bash
upstream backend {
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080;
}
server {
    location / {
        proxy_pass http://backend;
    }
}
```

Least connections load balancing: sends new requests to the server with fewest active connections.

```bash
upstream backend {
    least_conn;
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
}
```

IP hash load balancing: routes the same client IP always to the same backend (session persistence).

```bash
upstream backend {
    ip_hash;
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
}
```

Weighted load balancing with a backup server. The backup only receives requests when primary servers are down.

```bash
upstream backend {
    server 192.168.1.10:8080 weight=3;
    server 192.168.1.11:8080 weight=1;
    server 192.168.1.12:8080 backup;
}
```

Passive health checks: mark a server as failed after 3 failed attempts, then skip it for 30 seconds.

```bash
upstream backend {
    server 192.168.1.10:8080 max_fails=3 fail_timeout=30s;
    server 192.168.1.11:8080 max_fails=3 fail_timeout=30s;
}
```

## Logging

Define a custom log format named 'main' in the http block. This is the combined log format.

```bash
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                '$status $body_bytes_sent "$http_referer" '
                '"$http_user_agent" "$http_x_forwarded_for"';
```

Enable access logging with the 'main' format and error logging at warn level.

```bash
access_log /var/log/nginx/access.log main;
error_log  /var/log/nginx/error.log warn;
```

`access_log off;` — Disable access logging for a server or location block (useful for static assets).

```bash
access_log off;
```

`error_log /var/log/nginx/error.log debug;` — Set error log level to debug for verbose output. Levels: debug, info, notice, warn, error, crit, alert, emerg.

```bash
error_log /var/log/nginx/error.log debug;
```

JSON-formatted access log. Useful for log aggregation tools (ELK stack, Loki, etc.).

```bash
log_format json_combined escape=json
  '{"time": "$time_local", "remote_addr": "$remote_addr", '
  '"request": "$request", "status": $status, '
  '"bytes": $body_bytes_sent, "ua": "$http_user_agent"}';
```

## Variables Reference

`$host` — The request's Host header value (or server_name if no Host header is present).

```bash
proxy_set_header Host $host;
```

`$request_uri` — The full original request URI including query string, as sent by the client.

```bash
return 301 https://example.com$request_uri;
```

`$uri` — The current (possibly rewritten) request URI without query string.

```bash
try_files $uri $uri/ /index.php;
```

`$args  /  $query_string` — The query string part of the request URI (both variables are equivalent).

```bash
proxy_pass http://backend?$args;
```

`$remote_addr` — The IP address of the connecting client.

```bash
add_header X-Client-IP $remote_addr;
```

`$scheme` — The request scheme: 'http' or 'https'.

```bash
proxy_set_header X-Forwarded-Proto $scheme;
```

`$server_name` — The server_name of the server block that matched the request.

```bash
add_header X-Served-By $server_name;
```

`$http_<header>` — Access any request header. Hyphens become underscores: $http_user_agent, $http_x_forwarded_for.

```bash
if ($http_user_agent ~* 'bot') { return 403; }
```

<!-- PROSE:outro -->
## Conclusion

A well-considered nginx configuration determines the performance, security and stability of your site. Start with a clean server block, add TLS and security headers, and enable gzip and browser caching — then protect public endpoints against abuse with rate limiting. Test every change with `nginx -t` before reloading, and keep your TLS settings current and deliberate.

## Further Reading

- [nginx — official documentation](https://nginx.org/en/docs/) — complete directive reference
- [nginx Beginner's Guide](https://nginx.org/en/docs/beginners_guide.html) — getting started with configuration and concepts
- [Nginx — Wikipedia](https://en.wikipedia.org/wiki/Nginx) — background and history
<!-- PROSE:outro:end -->

## Related Commands

- [apache](https://www.jpkc.com/db/en/cheatsheets/web-servers/apache/) – the classic web server with modular configuration
- [caddy](https://www.jpkc.com/db/en/cheatsheets/web-servers/caddy/) – modern web server with automatic HTTPS
- [certbot](https://www.jpkc.com/db/en/cheatsheets/web-servers/certbot/) – issue and renew Let's Encrypt certificates

