Nginx Config Generator
Generate a production-ready nginx server block for a reverse proxy, static site, single-page app, or PHP - with HTTPS, gzip, security headers, and caching. Configure on the left, copy the config on the right.
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://example.com$request_uri;
}
server {
listen 443 ssl;
http2 on;
server_name www.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_prefer_server_ciphers off;
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:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
return 301 https://example.com$request_uri;
}
# example.com - generated by CtrlOps Nginx Config Generator
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
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_prefer_server_ciphers off;
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:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header X-XSS-Protection "1; mode=block" always;
client_max_body_size 20m;
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml image/svg+xml;
location / {
proxy_pass http://localhost:3000;
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 Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}sudo nano /etc/nginx/sites-available/example.com
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginxSave to sites-available, symlink into sites-enabled, test with nginx -t, then reload. Always run nginx -t before reloading.
What is an nginx server block?
nginx configuration is organized as a hierarchy: the top-level http block contains one or more server blocks (called "virtual hosts" or "server blocks"), and each server block contains location blocks that decide how to handle requests for different paths.
A server block is matched by its listen port and server_name. This generator produces one ready-to-use server block (plus a redirect block when you enable HTTPS or a www redirect).
sites-available, sites-enabled, and reloading
On Debian/Ubuntu, configs live in /etc/nginx/sites-available/ and are activated by symlinking them into /etc/nginx/sites-enabled/. The main nginx.conf includes everything in sites-enabled.
The golden rule: after any change, run sudo nginx -t to test the syntax, and only then sudo systemctl reload nginx. Reloading applies the new config with zero downtime; testing first prevents a typo from taking your site offline.
Reverse proxy headers explained
When nginx proxies to a backend app, the backend sees nginx as the client unless you forward the original request details. These headers do that:
| Header | Why it matters |
|---|---|
| Host | Passes the original domain so the app routes and builds URLs correctly. |
| X-Real-IP | The client's real IP, not nginx's. |
| X-Forwarded-For | The chain of client/proxy IPs. |
| X-Forwarded-Proto | Whether the original request was http or https. |
For WebSockets, nginx also needs proxy_http_version 1.1 and the Upgrade/Connection headers - enable the WebSocket toggle and the generator adds them.
try_files and the SPA fallback
A single-page app (React, Vue, Svelte) handles routing in the browser, so every URL must return index.html and let the app take over. That is what try_files $uri $uri/ /index.html; does: serve the file if it exists, otherwise fall back to index.html.
Use the plain static type instead (try_files $uri $uri/ =404;) for a normal website where missing files should genuinely 404.
HTTPS with Let’s Encrypt
The easiest way to get a free certificate is certbot. Once issued, the cert and key live at /etc/letsencrypt/live/your-domain/fullchain.pem and privkey.pem- exactly the paths this generator fills in when you pick Let's Encrypt. Certbot also auto-renews them.
Turn on the HTTP-to-HTTPS redirect so visitors always land on the secure version. Add HSTS only once HTTPS works reliably - it tells browsers to refuse HTTP for a long time, which is hard to undo if your certificate later breaks. Need to lock down file permissions on your keys? See our chmod calculator.
Common gotchas
- 413 Request Entity Too Large. nginx caps request bodies at 1 MB by default. Raise
client_max_body_size(e.g.20m) to allow larger uploads. - Trailing slash on proxy_pass.
proxy_pass http://app/(with a slash) rewrites the path; without it, the full URI is passed through. The difference matters. - root vs alias.
rootappends the full request path to the directory;aliasreplaces the matched location prefix. Mixing them up serves the wrong files. - Location order. Exact and regex locations are evaluated by specific rules, not top-to-bottom - put more specific matches where nginx expects them.
Common nginx directives
| Directive | What it does |
|---|---|
| listen | The port and protocol the server block accepts, e.g. 80 or 443 ssl. |
| server_name | The domain(s) this block matches, e.g. example.com www.example.com. |
| root | The filesystem directory files are served from. |
| location | Rules that apply to matching request paths. |
| proxy_pass | Forwards a request to a backend (reverse proxy). |
| try_files | Tries each path in turn, falling back to the last - the basis of SPA routing. |
| return 301 | Issues a permanent redirect, e.g. HTTP to HTTPS. |
| ssl_certificate | Path to the TLS certificate (with ssl_certificate_key for the key). |
| add_header | Adds a response header (security, caching, HSTS). |
| client_max_body_size | Largest request body nginx accepts (raise to fix 413 errors). |
Frequently asked questions
Related developer tools
Deploy your app without the YAML hell.
CtrlOps deploys Node.js, Next.js, and React apps with one click - it provisions the server, wires up nginx and SSL, and ships your code, all from a local-first desktop app with your credentials encrypted on your own machine.
✓ Start instantly·✓ No credit card·✓ No sneaky autorenewals

