
Table of Contents
By Khimananda Oli | Last reviewed: August 2026
You need to set up a reverse proxy with Nginx when your application requires SSL termination, load balancing, or protection from direct client exposure. Unlike a forward proxy that acts on behalf of clients, a reverse proxy sits in front of your backend servers, handling TLS, caching, and request routing while keeping your infrastructure topology hidden. This guide walks through the exact configuration patterns I use in production across AWS, Azure, and on-premise Ubuntu servers.
How Do You Set Up a Reverse Proxy with Nginx for Basic Traffic Forwarding?
The foundation of any reverse proxy configuration is the relationship between the frontend listener and the backend upstream. Before writing config files, ensure you have completed the prerequisite steps to install Nginx on Ubuntu and verified that your firewall allows ports 80 and 443. A common mistake is editing /etc/nginx/nginx.conf directly; instead, create modular site configurations in /etc/nginx/sites-available/ to maintain auditability and simplify rollbacks.
Define the Upstream Backend Pool
The upstream directive tells Nginx where to send proxied traffic. Place this outside your server block, typically at the top of your site configuration file or in a dedicated /etc/nginx/conf.d/upstreams.conf.
upstream backend_app {
least_conn;
server 127.0.0.1:8080 weight=3;
server 127.0.0.1:8081 weight=2;
server 192.168.1.50:8080 backup;
keepalive 32;
} - least_conn: Routes new requests to the backend with the fewest active connections, preventing overload on slow endpoints.
- weight: Distributes traffic proportionally; useful when backends have different CPU/RAM allocations.
- backup: Only receives traffic when all primary servers are unavailable — ideal for DR sites or overflow capacity.
- keepalive: Maintains persistent connections to backends, reducing TCP handshake overhead by up to 40% under load.
Create the Server Block with Proxy Pass
This minimal configuration forwards all requests to the upstream pool while preserving essential client metadata.
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
proxy_pass http://backend_app;
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 "";
}
} The proxy_http_version 1.1 and empty Connection header are critical when using keepalive connections to upstreams. Without them, Nginx defaults to HTTP/1.0 and closes connections after each request, negating the performance benefit of the keepalive directive. Always test configuration syntax with sudo nginx -t before reloading.
How Do You Configure SSL Termination and Security Headers in Nginx?
SSL termination at the reverse proxy layer offloads cryptographic work from your application servers, simplifying certificate management and enabling centralized security policies. After you set up free SSL with Let's Encrypt and Certbot, harden the TLS configuration beyond Certbot's defaults.
Harden TLS Parameters for Modern Browsers
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_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; Disabling session tickets prevents forward secrecy compromise if server memory is dumped. OCSP stapling eliminates an extra round-trip to the CA during TLS handshake, improving Time-to-First-Byte by 50–100ms for users in regions with high latency to certificate authorities — a tangible win for audiences in Nepal connecting to global CAs.
Inject Mandatory Security Headers
Add these inside your HTTPS server block to mitigate XSS, clickjacking, and MIME-type sniffing attacks:
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;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';" always; The always parameter ensures headers are added even on error responses (4xx, 5xx). Omitting it is a frequent oversight that leaves error pages unprotected. Validate your header configuration using browser dev tools or curl -I before deploying to production.
How Does Nginx Compare to Other Reverse Proxy Solutions in 2026?
Choosing the right reverse proxy depends on your operational context. While Nginx dominates traditional deployments, newer alternatives serve specific niches. Understanding these trade-offs prevents costly re-architecture later.
| Feature | Nginx | Caddy | Traefik | HAProxy |
|---|---|---|---|---|
| Automatic HTTPS | Via Certbot/script | Built-in ACME | Built-in ACME | Via external tool |
| Configuration Style | Declarative file | Caddyfile / JSON API | Dynamic discovery | Declarative file |
| Kubernetes Integration | Ingress Controller | Ingress Controller | Native CRDs | Ingress Controller |
| Max Concurrent Connections | ~100K+ per worker | ~50K estimated | ~70K estimated | ~200K+ optimized |
| L7 Load Balancing Algorithms | Round-robin, least_conn, ip_hash | Round-robin, least_conn | Round-robin, WRR, dynamic | All + custom Lua |
| Memory Footprint (idle) | ~5–10 MB | ~30–50 MB | ~60–100 MB | ~10–20 MB |
| Best For | Static + app proxy, caching | Auto-TLS, simple setups | Cloud-native, K8s dynamic | Pure L4/L7 LB, HA |
In practice, Nginx remains the default for teams needing fine-grained control over caching, header manipulation, and legacy protocol support. Caddy excels when automatic certificate renewal is the primary concern and configuration complexity must stay minimal. Traefik shines in Kubernetes environments where service discovery drives routing rules dynamically. HAProxy is unmatched for pure load-balancing throughput and advanced health-check logic but lacks built-in web-serving capabilities. For most full-stack applications requiring both static asset serving and API proxying, Nginx offers the best balance of performance, flexibility, and ecosystem maturity.
How Do You Optimize Nginx Reverse Proxy Performance Under High Load?
Default Nginx settings assume modest traffic. Production systems serving thousands of concurrent users require explicit tuning of buffers, timeouts, and connection pools. These optimizations apply whether you're running on a single VPS or behind an AWS ALB.
Tune Buffer Sizes and Timeouts
# In http or server context
client_body_buffer_size 16k;
client_max_body_size 50m;
client_header_buffer_size 1k;
large_client_header_buffers 4 8k;
proxy_buffer_size 8k;
proxy_buffers 16 8k;
proxy_busy_buffers_size 16k;
proxy_connect_timeout 5s;
proxy_send_timeout 30s;
proxy_read_timeout 60s; Undersized buffers cause Nginx to write temporary files to disk, destroying latency. Oversized buffers waste RAM under concurrency. The values above suit typical web APIs and Laravel/Node.js applications. For file-upload-heavy services, increase client_max_body_size and consider streaming uploads with proxy_request_buffering off to avoid memory spikes.
Enable Response Caching for Idempotent Endpoints
# In http context
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=app_cache:10m max_size=1g inactive=60m use_temp_path=off;
# In location block
location /api/public/ {
proxy_cache app_cache;
proxy_cache_valid 200 10m;
proxy_cache_valid 404 1m;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
add_header X-Cache-Status $upstream_cache_status;
proxy_pass http://backend_app;
} Caching at the reverse proxy layer shields backends from repetitive reads. The use_temp_path=off directive writes cache files directly to their final destination, avoiding an extra filesystem move operation. Monitor cache hit ratios via the X-Cache-Status header during staging validation. For deeper observability into cache performance and backend latency, integrate with your existing stack as described in the Prometheus and Grafana full monitoring stack guide.
How Do You Implement Health Checks and Failover in Nginx Upstreams?
Passive health checks (relying on failed requests) cause user-visible errors during failover. Active health checks probe backends continuously, removing unhealthy nodes before traffic reaches them. Note that active checks require Nginx Plus or the open-source nginx_upstream_check_module. For standard open-source Nginx, combine passive checks with robust error handling.
Configure Passive Health Checks with Fallback
upstream backend_app {
server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8081 max_fails=3 fail_timeout=30s;
server 192.168.1.50:8080 backup;
}
server {
location / {
proxy_pass http://backend_app;
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 2;
proxy_next_upstream_timeout 10s;
}
} The max_fails=3 fail_timeout=30s pair marks a server unavailable after three failures within 30 seconds, then retries it after the timeout expires. proxy_next_upstream defines which response codes trigger failover to the next healthy backend. Setting proxy_next_upstream_tries 2 limits retry chains, preventing cascading delays when multiple backends degrade simultaneously.
Add Application-Level Health Endpoints
Expose a lightweight /health endpoint in your application that verifies database connectivity and critical dependencies. Configure Nginx to bypass caching and skip rate limiting for this path:
location = /health {
proxy_pass http://backend_app;
proxy_cache off;
limit_req_status 200;
access_log off;
allow 10.0.0.0/8;
allow 172.16.0.0/12;
deny all;
} Restricting access to internal CIDRs prevents external actors from probing your health endpoint. External load balancers (AWS ALB, GCP LB) should target this endpoint for their own health checks, creating a two-tier verification system that catches both network-level and application-level failures.
Production Checklist for Your Nginx Reverse Proxy Deployment
Before marking your setup complete, verify these items against your live configuration. Skipping any one has caused incidents I've personally responded to at 3 AM.
- Syntax validation: Run
sudo nginx -tafter every change. Automate this in CI/CD pipelines that deploy config via Ansible or Terraform. - Reload safety: Use
sudo systemctl reload nginxinstead of restart to preserve existing connections during config updates. - Log rotation: Confirm
/etc/logrotate.d/nginxexists and rotates access/error logs daily. Unrotated logs fill disks silently. - Worker sizing: Set
worker_processes auto;andworker_connections 1024;minimum. Monitor withstub_statusmodule to detect connection saturation. - Security audit: Scan headers with Mozilla Observatory or securityheaders.com. Fix any grade below B before going live.
- Backup config: Version-control all Nginx configs in Git. Tag releases matching deployment timestamps for instant rollback correlation.
A properly configured reverse proxy is invisible when working correctly and catastrophic when misconfigured. Treat your Nginx configuration with the same rigor as application code: review, test, version, and monitor it continuously.
Next Steps for Your Reverse Proxy Infrastructure
You now have a production-grade foundation to set up a reverse proxy with Nginx that handles SSL, load balancing, caching, and security enforcement. The next logical steps depend on your scale: implement structured logging for request tracing, add rate limiting to prevent abuse, or integrate with service mesh for east-west traffic control. If your team needs hands-on assistance architecting or auditing Nginx deployments across multi-cloud or compliance-regulated environments, reach out to discuss your infrastructure requirements.