Skip to content
Control Plane Labs

Nginx and Caddy Config Generator

Fill in a domain and an upstream, get a working nginx server block and an equivalent Caddyfile side by side, with TLS, headers, and rate limiting.

Privacy: Everything runs in your browser. Your domains, upstreams, and file paths are never uploaded or logged.

What it serves
TLS
Optional features

nginx.conf

Caddyfile

Generated entirely in your browser — nothing is uploaded. Run nginx -t or caddy validate before you reload, and read the comments: a few directives need modules or plugins that are not in the stock builds.

What it does

You describe the site once — domains, whether it proxies to an app or serves files off disk, how TLS is handled, and which of the usual extras you want — and the tool emits two configs at the same time: an nginx server block and a Caddyfile that do the same job. Both panes have their own copy button, and the whole form is encoded in the URL so a config you built is a link you can paste into a ticket.

The toggles cover the things people actually forget: HTTP to HTTPS redirect, gzip, the four security headers worth setting unconditionally, per-IP rate limiting, WebSocket upgrade plumbing, and basic auth. Static and SPA modes differ in exactly one line — the SPA fallback — but that one line is the difference between deep links working and every refresh returning 404.

How it works

Everything is a pure function of the form state, evaluated in your browser. There is no template server, no account, and no request logged anywhere. Two builders run on every keystroke: one walks the nginx directive tree and one walks the Caddyfile.

The generators are opinionated where the two servers genuinely differ rather than pretending they are interchangeable. In reverse-proxy mode the nginx output declares an upstream block with keepalive 32 and sets the four forwarding headers by hand, because ngx_http_proxy_module does not forward the client address unless you tell it to. The Caddy equivalent is a single reverse_proxy line, since Caddy sets X-Forwarded-For and X-Forwarded-Proto on its own.

TLS is the other place the two diverge sharply. Picking Let’s Encrypt in nginx gives you a port 80 server block that keeps /.well-known/acme-challenge/ reachable over plain HTTP — a requirement of the HTTP-01 challenge in RFC 8555 — plus explicit ssl_certificate paths under /etc/letsencrypt/live/. The same choice in Caddy produces a two-line global options block and nothing else, because automatic HTTPS provisions the certificate, renews it, and installs the redirect without configuration.

Directives that will not work where you paste them

Read the comments in the nginx pane before you reload. Two of the generated directives are only legal in http{} scope, not inside a server{} block: limit_req_zone from ngx_http_limit_req_module, and the map $http_upgrade $connection_upgrade block that the documented WebSocket pattern depends on. Drop them in a file under conf.d/ and keep the server block clean. If you paste the file wholesale into sites-available/ you get "limit_req_zone" directive is not allowed here and a failed reload.

Two features are also honestly unavailable in stock builds, and the tool says so instead of emitting config that will not load. Brotli in nginx needs the out-of-tree ngx_brotli module. Rate limiting in Caddy needs a third-party module compiled in with xcaddy; the directive is emitted commented out so caddy validate still passes. Basic auth in Caddy takes a bcrypt hash, not a password — generate it with caddy hash-password and paste the result over the placeholder.

When to reach for it

The obvious case is standing up a new vhost and not wanting to copy the last one and miss a server_name. The more useful case is migration: if you are weighing a move from nginx to Caddy, generating both halves of the same config side by side shows you concretely how much of your nginx file is ceremony that Caddy handles for you, and which parts (rate limiting, Brotli) you would actually lose.

It is also a decent teaching artifact. The SPA mode makes the try_files fallback and the cache-control split between hashed assets and index.html visible in one screen, which is easier to explain than prose.

Frequently asked questions

Why does the nginx output put some directives outside the server block?+
limit_req_zone and the map used for WebSocket upgrades are only valid in http{} scope, not inside server{}. If you paste the whole file into sites-available/ without moving them, nginx fails to start with "limit_req_zone" directive is not allowed here. Put them in a file under conf.d/ instead.
The Caddyfile is so much shorter. Am I missing something?+
No. Caddy defaults to automatic HTTPS, HTTP-to-HTTPS redirects, OCSP stapling, and modern cipher selection, so there is nothing to write down. The nginx output looks longer because every one of those behaviours has to be spelled out explicitly.
Why is rate limiting commented out in the Caddyfile?+
Caddy has no rate_limit directive in the standard distribution. It comes from a third-party module you have to compile in with xcaddy. Rather than emit config that fails caddy validate, the tool leaves it commented with the plugin name.
Does gzip cover Brotli too?+
Not in nginx. gzip on is built in, but Brotli requires the out-of-tree ngx_brotli module, so it is emitted as a comment. Caddy's encode zstd gzip ships in the standard build; Zstandard and gzip cover essentially every client, and Brotli support in Caddy is compression-only for pre-compressed files.
Will this config pass a TLS scanner?+
The TLS block sets TLSv1.2 and TLSv1.3 only, disables session tickets, and turns on OCSP stapling when Let's Encrypt paths are used, which is enough for an A on most scanners. It deliberately does not pin a cipher list — leaving ssl_prefer_server_ciphers off lets the client choose, which is the current recommendation for TLS 1.3.