What you will achieve
Nginx as a reverse proxy to backend apps (Node, PHP-FPM, Docker) on Ubuntu/Debian — TLS termination and WebSocket pass-through.
1) Install Nginx
sudo apt install nginx
sudo systemctl enable --now nginx
2) Site config
# /etc/nginx/sites-available/app
server {
listen 80;
server_name app.example.com;
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 Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
sudo ln -s /etc/nginx/sites-available/app /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
3) Add TLS with Certbot
See the Certbot guide for Let's Encrypt automation.
Verify
curl -I -H "Host: app.example.com" http://127.0.0.1
sudo ss -tlnp | grep nginx
5) Rate limiting
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
server {
limit_req zone=one burst=20 nodelay;
}
6) Upstream keepalive
upstream backend {
server 127.0.0.1:3000;
keepalive 32;
}
7) Static file caching
location /static/ {
alias /var/www/static/;
expires 30d;
}
Logging
tail -f /var/log/nginx/access.log
sudo nginx -T | less
nginx -T dumps full effective config — invaluable when includes obscure the active server block.
8) WebSocket timeout tuning
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
Prerequisites
Backend app listening on localhost port. DNS A record if public. nginx package. Optional certbot for TLS. Understanding of proxy headers for apps that need real client IP.
Default site cleanup
sudo rm /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx
proxy_buffering off for SSE
proxy_buffering off;Server-sent events and long polling need buffering disabled or clients see delayed streams.
upstream health checks
Commercial nginx plus or use separate health checker — open source nginx needs max_fails and fail_timeout on upstream server lines for passive health awareness when backend dies.
client_max_body_size uploads
client_max_body_size 100M;Default 1M breaks file upload apps behind proxy — set in server or location block matching app requirements.
real_ip from CDN
Cloudflare connecting IP in CF-Connecting-IP header — set_real_ip_from cloudflare IP ranges and real_ip_header for accurate access logs behind CDN.
map directive for websocket upgrade
Some configs need map $http_upgrade $connection_upgrade block — copy from nginx wiki websocket example when proxy_pass returns 502 on websocket only while HTTP works.
ssl_stapling when TLS terminates
After certbot, enable stapling in nginx ssl block — reduces TLS handshake latency for returning visitors behind your reverse proxy.
include sites-enabled
Default nginx.conf includes sites-enabled — verify symlink not broken after manual site rename.
Health check endpoint
Expose /healthz returning 200 from backend — external monitor hits nginx which proxies to app — catches backend death even when nginx process still running.