Skip to content

🟩 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.

100% Free No signup Browser-only 5 languages Dark mode

🔒 About Privacy

🎛 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 environmentit 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 firstwriting 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 checka 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 discardedthey 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 warningyou find out when a scanner tells you the headers are missing. The same rule applies to proxy_set_headeradd one inside a location and every proxy header set above it disappears. Start by checking the actual response headers with curl -Iwhat 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 itduplicated, but far better than disappearing. And add always to your add_header directiveswithout 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 tooproxy_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 1MBan 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 themone 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 itchange 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.logthe 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 reloadwhen 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 reloadrestarting 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.pemwith 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 controla change edited directly on the production server disappears silently at the next deploy.

📖 How to Use

  1. 1
    Pick a preset
    Select Static, WordPress, Laravel or Next.js — recommended features get auto-checked.
  2. 2
    Set host and SSL paths
    Enter server_name, document root, and Lets Encrypt fullchain.pem / privkey.pem paths.
  3. 3
    Copy or download
    Use the buttons to copy to clipboard or download as a .conf file.
  4. 4
    Place into nginx and reload
    Place 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?
Use certbot from Lets Encrypt to issue a free certificate: sudo certbot --nginx -d example.com -d www.example.com. Only enable HSTS after HTTPS is fully working.
nginx -t reports an error
Most issues are SSL path typos or a PHP-FPM socket path that does not match your install. Check the line number in the error and run journalctl -u nginx.
Can I use Apache .htaccess on nginx?
No. nginx does not read .htaccess — rewrites must live in server / location blocks. For Apache hosts, use our .htaccess generator instead.
🐛 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.

* Browser info (UA / screen / language / URL) is sent automatically to help reproduce the issue