Reverse Proxy with nginx
Reverse Proxy with nginx
Section titled “Reverse Proxy with nginx”A reverse proxy sits in front of your application servers and handles TLS, routing, caching, and access control so the apps behind it can stay simple HTTP services on localhost. nginx is the workhorse of self-hosting: fast, battle-tested, and configurable to a fault.
This page covers the patterns you actually need in production. For a zero-open-ports alternative that tunnels out to Cloudflare’s edge, see Reverse Proxy with Cloudflared; for a Docker-label-driven proxy, see Reverse Proxy with Traefik.
Basic proxy with TLS termination
Section titled “Basic proxy with TLS termination”Terminate TLS at nginx and forward plain HTTP to the upstream. Redirect all HTTP to HTTPS, and pass through the headers your app needs to know its real client and scheme.
upstream app_backend { server 127.0.0.1:8080; keepalive 32;}
server { listen 80; server_name app.example.com; return 301 https://$host$request_uri; # force HTTPS}
server { listen 443 ssl; http2 on; server_name app.example.com;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
location / { proxy_pass http://app_backend; 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; }}Obtain and auto-renew the certificate with Certbot or
acme.sh. Generate modern ssl_protocols/ssl_ciphers settings with the
Mozilla SSL Configuration Generator rather than
hand-rolling them.
Security headers
Section titled “Security headers”Set protective response headers once in a shared snippet and include it in each
server block:
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;add_header X-Content-Type-Options "nosniff" always;add_header X-Frame-Options "SAMEORIGIN" always;add_header Referrer-Policy "strict-origin-when-cross-origin" always;Rate limiting
Section titled “Rate limiting”Throttle abusive clients with a shared-memory zone. Define the zone at http scope,
apply it where needed with a small burst:
# http { } contextlimit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
# location { } contextlimit_req zone=api burst=20 nodelay;Caching
Section titled “Caching”Cache upstream responses to absorb load and speed up repeat requests:
# http { } contextproxy_cache_path /var/cache/nginx levels=1:2 keys_zone=site:10m max_size=1g inactive=60m;
# location { } contextproxy_cache site;proxy_cache_valid 200 10m;add_header X-Cache-Status $upstream_cache_status; # HIT / MISS for debuggingWebSocket proxying
Section titled “WebSocket proxying”WebSockets need the Upgrade/Connection handshake headers forwarded, or the
connection drops to plain HTTP. Use a map so the Connection header is correct for
both upgrade and normal requests:
# http { } contextmap $http_upgrade $connection_upgrade { default upgrade; '' close;}
# location { } contextproxy_pass http://app_backend;proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection $connection_upgrade;proxy_read_timeout 3600s; # long-lived socketsValidate before reloading
Section titled “Validate before reloading”Always test the config and reload (not restart) so live connections aren’t dropped:
nginx -t && systemctl reload nginxnginx vs Cloudflare Tunnel
Section titled “nginx vs Cloudflare Tunnel”| nginx | Cloudflare Tunnel | |
|---|---|---|
| Inbound ports | Must open 80/443 to the internet | None — outbound tunnel only |
| TLS certs | You manage (Certbot/acme.sh) | Cloudflare-managed at the edge |
| DDoS / WAF | Self-managed | Cloudflare edge included |
| Works offline / LAN-only | Yes | No (depends on Cloudflare) |
| Best for | Full control, on-prem, high throughput | Hiding origin IP, no port-forwarding |
Many self-hosters run both: Cloudflare Tunnel for public ingress, nginx behind it for routing and caching across local services.
Related
Section titled “Related”- Reverse Proxy with Traefik — Docker-native alternative with automatic certificates
- Load Balancing with HAProxy — when one upstream isn’t enough
- Reverse Proxy with Cloudflared — zero-open-port ingress
- Identity-Aware Proxies — adding authentication in front of apps