🟩 nginx.conf Generator
Enter your host, root directory and SSL paths, then toggle features. Generates a production-ready server block: HTTPS redirect, HTTP/2, modern TLS, HSTS, gzip / brotli, static cache, PHP-FPM, reverse proxy, WebSocket and try_files in real time.
🔒 About Privacy
- ・All generation runs entirely inside your browser
- ・Host names, paths and settings are never sent to a server
- ・No logs, no tracking, no database
🎛 Presets
🧩 Choose features
📄 Generated nginx.conf
📖 Where people get stuck
Generates an nginx.conf including the server block for setups such as a static site, a reverse proxy or PHP-FPM, emitting SSL, gzip, caching and security header directives along with it. Everything runs in the browser. What it can generate is syntax, not a guarantee that it works in your environment — it cannot verify that a certificate path exists, that the upstream is reachable, or that the document root is right. And time lost to nginx is usually not spent on a directive you mistyped, but on a directive whose meaning to nginx differed from the meaning you had in mind.
| Case | What happens | What to do |
|---|---|---|
location is not evaluated in the order you wrote it |
nginx does not try location blocks from the top down; it selects one by a fixed precedence. First an exact match with =, then the longest prefix match (settled immediately if it carries ^~), then regular expressions (~ and ~*) in the order written, and if none match, the longest prefix match held in reserve. So moving blocks up and down often changes nothing, while adding a single regex block quietly steals requests that a prefix block used to handle. The classic symptom is static files being handed to PHP, or the reverse, and it happens when the relationship between location ~ \.php$ and location /assets/ is misunderstood. |
Do not guess — run nginx -T. It prints the final configuration with every include expanded, so an oversight such as the same location also existing in another file becomes visible immediately. As a design principle, settle static files first — writing location ^~ /assets/ stops regex evaluation right there. Write URLs that resolve to exactly one thing, such as the home page or favicon.ico, with =: it is the fastest and the intent is unambiguous. After any change, request concrete URLs with curl and check — a configuration that reads correctly and the block nginx actually selects are two different questions. |
add_header is replaced, not inherited |
nginx inheritance has a counter-intuitive rule. If a child block writes even one directive of a given kind, every directive of that kind from the parent is discarded — they are replaced, not added to. The damage is worst with add_header: set five security headers in the server block, add a single cache-control line inside location ~ \.php$, and all five security headers vanish from PHP responses. There is no error and no warning — you find out when a scanner tells you the headers are missing. The same rule applies to proxy_set_header — add one inside a location and every proxy header set above it disappears. |
Start by checking the actual response headers with curl -I — what the configuration says and what comes back are different things. There are two remedies. Keeping headers in one place and never writing add_header in a child block is the simplest. Where that is impossible, factor the header set into a separate file and include it in each location that needs it — duplicated, but far better than disappearing. And add always to your add_header directives — without it, the headers are not attached to 4xx or 5xx responses (error pages often being exactly what you wanted to protect). The same caution applies to proxy_set_header. |
| Behind a reverse proxy, headers and sizes break | Writing proxy_pass alone leaves your application unaware of which hostname and protocol it was called with. Without passing Host and X-Forwarded-Proto, the application generates URLs like http://localhost:3000/..., redirects loop, and cookie domains go wrong. The trailing slash on proxy_pass changes the meaning too — proxy_pass http://app; passes the original path through, while proxy_pass http://app/; strips the location prefix first. That one character is the difference between working and a 404. Default sizes and timeouts deserve attention as well: the default client_max_body_size is 1MB — an upload larger than that is rejected with a 413 before it ever reaches your application. And WebSocket will not connect unless you set Upgrade and Connection explicitly. |
Write at least these four in a proxying location: proxy_set_header Host $host;, X-Real-IP $remote_addr;, X-Forwarded-For $proxy_add_x_forwarded_for; and X-Forwarded-Proto $scheme;. Then enable the trusted-proxy setting in your application so it honours them — one side alone has no effect. If you handle uploads, align client_max_body_size with the limit in your application, and if you have long-running requests, raise proxy_read_timeout (60 seconds by default) alongside it — change only one and the shorter of the two still cuts you off. Apply changes with nginx -t && nginx -s reload, and when you get a 502, read error.log rather than access.log — the cause is almost always written there. |
Editing the file alone changes nothing. nginx reads its configuration at startup and then holds it, so saving the file changes no behaviour until you reload — when you think you fixed it and it is still broken, check first whether you reloaded. The order is always nginx -t to check the syntax, then reload — restarting without checking means that if the configuration is wrong, nginx fails to start and the site goes down completely (whereas a reload keeps serving with the old configuration when the new one is invalid). One note on certificates: point ssl_certificate at fullchain.pem, not cert.pem — with the intermediate certificate missing, it looks fine in a browser while curl, mobile applications and older environments fail to validate. Because that fault does not reproduce in your own environment, you cannot notice it until someone reports it. Finally, keep the configuration files under version control — a change edited directly on the production server disappears silently at the next deploy.
📖 How to Use
-
1
Pick a presetSelect Static, WordPress, Laravel or Next.js — recommended features get auto-checked.
-
2
Set host and SSL pathsEnter server_name, document root, and Lets Encrypt fullchain.pem / privkey.pem paths.
-
3
Copy or downloadUse the buttons to copy to clipboard or download as a .conf file.
-
4
Place into nginx and reloadPlace into /etc/nginx/sites-available/, symlink into sites-enabled, run sudo nginx -t to test, then sudo systemctl reload nginx.
❓ Frequently Asked Questions
What if I do not have an SSL certificate yet?
sudo certbot --nginx -d example.com -d www.example.com. Only enable HSTS after HTTPS is fully working.nginx -t reports an error
journalctl -u nginx.Can I use Apache .htaccess on nginx?
🔗 Related Tools
🐛 Found a bug or issue with this tool?
Free to use, no signup. Even just the steps to reproduce are helpful. Reports go directly to the operator and help us fix issues.
Thanks for your report!
Your report has been delivered to the operator and will be used to improve the tool.