Nginx Config Snippets
Generate nginx server blocks for reverse proxy, static sites, full-stack apps, and containers. HTTPS, security headers, WebSocket, and gzip handled.
Template
Server
Backend
Headers & misc
# Redirect plain HTTP to HTTPS
server {
listen 80;
listen [::]:80;
server_name example.com;
# ACME challenge for Let's Encrypt — leave plain HTTP open for /.well-known
location /.well-known/acme-challenge/ {
root /var/www/html;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com;
# TLS — paths from Let's Encrypt by default; replace if you use another CA.
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Modern TLS profile (Mozilla SSL Configuration Generator output)
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:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# OCSP stapling — verify cert revocation without phoning home
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s;
# Security headers — applied to every response
add_header Strict-Transport-Security "max-age=31536000; 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;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
client_max_body_size 10M;
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/javascript application/javascript application/json application/xml image/svg+xml;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log warn;
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;
proxy_set_header X-Forwarded-Host $host;
}
}
Save under /etc/nginx/sites-available/, symlink to sites-enabled/, thennginx -t && systemctl reload nginx. After deployment, check the TLS profile at ssllabs.com/ssltest.
About this tool
nginx is the default reverse proxy and static server for most production stacks. Its config language is well-documented but verbose, and the right defaults change every few years — TLS cipher suites, gzip vs Brotli, HTTP/2 vs HTTP/3 listen directives. This generator produces a sensible modern starting point for the common shapes.
Reverse proxy forwards everything to a backend
app server (Node, Rails, Spring, FastAPI). Static
serves a directory directly, with optional SPA fallback to
/index.html for client-side routing.
Static + backend tries the filesystem first and
falls through to the backend — the canonical setup for most
single-page apps with an API. Container front is
reverse proxy preconfigured for a containerised backend.
The HTTPS path uses Mozilla's Modern TLS profile (TLS 1.2 + 1.3, AEAD ciphers only, OCSP stapling). The HSTS, X-Content-Type-Options, X-Frame-Options, Referrer-Policy, and Permissions-Policy headers are emitted with sane defaults — comment out anything that doesn't fit your application. Content-Security- Policy is intentionally not generated here because the right CSP is app-specific; use a tool like csp-evaluator.withgoogle.com to build one.
After deploying the generated config, validate with
nginx -t and reload with
systemctl reload nginx. Then run your TLS through
ssllabs.com/ssltest
and your headers through
securityheaders.com
— both are free and confirm the config does what you think.
Frequently asked questions
What is the difference between root and alias?
`root` prepends to the URL path: `root /var/www; location /img { … }` serves /var/www/img/foo.jpg for /img/foo.jpg. `alias` replaces the location prefix: `location /img { alias /var/static; }` serves /var/static/foo.jpg for /img/foo.jpg. Use root unless you specifically need alias semantics.
How do I enable HTTPS with Let’s Encrypt?
Run certbot to obtain a cert (certbot --nginx is easiest), then point ssl_certificate and ssl_certificate_key at the produced fullchain.pem and privkey.pem. The redirect server block in this generator keeps /.well-known/acme-challenge open on plain HTTP so future renewals work.
When should I use proxy_pass vs try_files?
try_files looks at the filesystem first and serves whatever it finds; use it for static sites and the static portion of mixed setups. proxy_pass forwards the request to a backend application; use it for dynamic content. The Static + backend template combines them — try_files first, fall through to proxy_pass.
How do I handle WebSockets?
Toggle "WebSocket upgrade headers." nginx needs proxy_http_version 1.1 plus the Upgrade and Connection headers — without them, the WebSocket handshake fails and clients see HTTP 400. Increase proxy_read_timeout if your sockets are long-lived.
What security headers should I always include?
Strict-Transport-Security (HSTS) once HTTPS is real and you’re committed to it. X-Content-Type-Options nosniff. Referrer-Policy strict-origin-when-cross-origin. X-Frame-Options is older but still useful — newer apps prefer CSP frame-ancestors which this generator does not include because the right value is app-specific.
What is the difference between nginx reload and restart?
reload sends SIGHUP — nginx reads the new config and replaces worker processes one at a time without dropping connections. restart actually stops and starts the process; it has a brief downtime window. After every config change, run nginx -t first to validate, then reload.