Caddy server: automatic HTTPS, the Caddyfile, and when it is the wrong web server
Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
At a glance
- What is it?
- Caddy is a Go web server that provisions and renews TLS certificates by default, configured through the Caddyfile or native JSON. It is a strong fit for small teams that want HTTPS without managing certbot, and a poor fit for anyone who needs a plugin that does not exist yet.
- Who is it for?
- Adopt Caddy if you want TLS certificates issued and renewed without a separate ACME client and you are comfortable with either the Caddyfile or the JSON config. Do not adopt it if you depend on a web server module that has no Caddy equivalent, or if you cannot rebuild the binary to add one.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 27, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The problem Caddy solves: TLS certificates that renew themselves
Most web servers treat HTTPS as something you bolt on afterwards. You install the server, then install an ACME client, then wire the client into a renewal timer, then remember to reload the server when the certificate changes. Caddy inverts that. The README states the project's position in one line: "Caddy is an extensible server platform that uses TLS by default." Automatic HTTPS is listed as a default behaviour, not an optional module.
The mechanism is a bundled library called CertMagic, which the README credits as the engine behind Caddy. CertMagic handles certificate issuance and renewal, and Caddy calls into it when a site block names a hostname. For public names Caddy obtains certificates from Let's Encrypt and ZeroSSL, with multi-issuer fallback if one authority fails. For internal names and IP addresses it runs a fully managed local CA instead of trying to reach a public authority.
That second case is the one people underestimate. A service on an internal hostname or a bare IP address normally cannot get a publicly trusted certificate at all. Caddy's answer is to issue from its own CA, and the README also mentions coordination with other Caddy instances in a cluster, which matters when several nodes terminate TLS for the same names and would otherwise race each other for certificates.
The audience is small and mid-sized operations that want HTTPS to be a property of the server rather than a maintenance task. It is also a reasonable fit for internal tooling, where the local CA removes the usual self-signed certificate warnings.
How Caddy is configured: Caddyfile, JSON, and config adapters
Caddy has two configuration surfaces and a bridge between them. The Caddyfile is the short human-readable format most examples use. Native JSON is the full-fidelity format that the server actually runs. Config adapters translate one into the other, so the Caddyfile is not a separate runtime mode but a front end that produces JSON.
The JSON API is the third piece. The README describes dynamic configuration through the JSON API, which means a running Caddy can be reconfigured over HTTP without a restart. The admin endpoint is how that happens, and because it can change a live server's behaviour, it is also the part you should not expose publicly.
Architecturally, Caddy is a modular core. The repository layout reflects this: the top level holds the core files (caddy.go, admin.go, listeners.go, logging.go, metrics.go, storage.go) while modules/ contains the HTTP server, TLS, and other components. The README describes this as a "modular architecture" that lets Caddy do anything without bloat, which is accurate in the sense that unused modules cost nothing at runtime, though they do cost binary size unless you build without them.
The protocol support is worth stating plainly: HTTP/1.1, HTTP/2, and HTTP/3 are all enabled by default. The go.mod file lists github.com/quic-go/quic-go, which is the QUIC implementation HTTP/3 requires. You do not enable HTTP/3 as an extra step.
One practical consequence of the adapter design: when you see a Caddyfile directive that does not behave as expected, you can inspect the JSON it produces rather than guessing. That is a debugging path nginx does not offer in the same form.
Installing Caddy and running a first reverse proxy
The README gives the simplest install path directly: download Caddy from GitHub Releases and place the executable file in your PATH. It then points to the online documentation for other install instructions, so the repository itself does not enumerate package manager commands. If you prefer to build, the documented requirement is Go 1.25.0 or newer, and the development build is two commands.
git clone "https://github.com/caddyserver/caddy.git"
cd caddy/cmd/caddy/
go buildThe README notes these steps will not embed proper version information, and points to the next section for that. It also warns that Caddy may try to bind to low ports, and on Linux gives this command to grant the binary permission:
sudo setcap cap_net_bind_service=+ep ./caddyFor a build that carries version information or extra plugins, the README points to the builder tool xcaddy, which automates the manual steps of copying main.go into a new folder, initializing a Go module, pinning a version, adding plugin imports, and compiling:
xcaddy buildThe documented compile step uses build tags to exclude some database drivers: go build -tags=nobadger,nomysql,nopgx. That detail matters if you are building a custom binary, because the tags are part of the command the README shows rather than an optional flourish.
For a first real use, the shape of a reverse proxy in the Caddyfile is a site address followed by a block containing a reverse_proxy directive pointing at your backend. The README's quick start section does not reproduce a full Caddyfile, and instead recommends the Getting Started guide on the project website for users of any experience level. That is the honest place to look for a working example rather than guessing at directive syntax.
Where Caddy gets in the way: plugins, binary size and the admin API
The extension model is the main constraint. Caddy plugins are compiled into the binary, not loaded at runtime. The README's build instructions show the pattern: copy Caddy's main.go, add imports for any custom plugins you want to add, then compile. There is no documented mechanism for dropping a shared object into a directory and restarting. If a plugin you need does not exist, you are writing it in Go, and if you are not a Go developer, that is a hard stop.
The flip side is that a stock binary cannot be extended after the fact. Adding one plugin means rebuilding, which means either installing xcaddy or following the manual steps, and then redeploying the binary everywhere it runs. Teams used to nginx's module ecosystem, where a distribution package may already include what you want, will notice the difference immediately.
The admin API is the other sharp edge. Dynamic configuration over HTTP is genuinely useful for automation, but it is an unauthenticated-by-default control surface in the sense that anything that can reach the endpoint can rewrite the running config. The README does not frame this as a risk; it simply lists the JSON API as a feature. Treating the admin port as an internal interface is the reader's job, not something the documentation enforces.
Finally, the README's claims about scale and production readiness (trillions of requests, hundreds of thousands of sites) are the project's own statements. They are not something this article can verify, and they should not be treated as a benchmark.
Caddy compared with nginx for a reverse proxy
The comparison people actually search for is Caddy against nginx, so it is worth being concrete about the difference rather than listing features.
nginx separates the two jobs. You configure the proxy, and separately you obtain certificates and tell nginx where the files live. Renewal is a scheduled task outside the server, and a failed renewal is invisible until a browser complains. Caddy merges those jobs: certificate management is inside the server process, driven by CertMagic, and the README's claim that Caddy "stays up when other servers go down due to TLS/OCSP/certificate-related issues" is a direct statement about that coupling.
The configuration models differ in kind, not degree. nginx configuration is a text format that is the only format. Caddy has a text format that adapts into JSON, and the JSON is what runs. That gives Caddy a programmable configuration path nginx lacks, at the cost of a second representation to understand when something goes wrong.
The ecosystem difference cuts the other way. nginx has a large set of third-party modules and years of operational documentation; Caddy's plugin set is smaller and its extension path requires Go. If your requirements are a plain reverse proxy with TLS termination and nothing exotic, Caddy removes an entire maintenance category. If your requirements include a specific nginx module, Caddy is the wrong tool and no amount of automatic HTTPS compensates.
Maintenance, licensing and what the repository tells you about cost
The repository is not archived. The last push was on 2026-06-03, which is the same date as the v2.11.4 release, so the release and the repository activity line up. Before that, v2.11.3 landed on 2026-05-12 and v2.11.2 on 2026-03-06. That is a release cadence measured in weeks to a couple of months across the visible window, not a project that has gone quiet.
Upgrade cost is where a compiled-in plugin model shows up on your calendar. Upgrading Caddy is not just replacing a binary if you built a custom one, because the custom binary has to be rebuilt with the same plugin imports against the new version. The README's build section includes an optional step to pin the Caddy version with go get github.com/caddyserver/caddy/v2@version, which is the mechanism that keeps a rebuild reproducible. Teams running the stock binary have a much simpler upgrade: download the new release and replace the file.
On licensing, Caddy is Apache-2.0, a permissive licence that permits commercial use and modification. That is a statement about the licence text, not legal advice, and it says nothing about the licences of the third-party modules in go.mod or of any plugin you compile in. Those are separate dependencies with their own terms, and a plugin author can choose differently from the core project. If you are shipping Caddy inside a product, the dependency licences are the part worth checking, not the core licence.
Editorial conclusion
Adopt Caddy if you want TLS certificates issued and renewed without a separate ACME client and you are comfortable with either the Caddyfile or the JSON config. Do not adopt it if you depend on a web server module that has no Caddy equivalent, or if you cannot rebuild the binary to add one. Before committing, verify that your required plugins exist, that you can run xcaddy build with them, and that your deployment can bind ports 80 and 443.
Frequently asked questions
What is Caddy used for in software?
Caddy is a multi-platform HTTP/1, HTTP/2 and HTTP/3 web server that acts as a reverse proxy and static file server, with TLS enabled by default. The README describes it as an extensible server platform that uses TLS by default, and automatic HTTPS is listed as a default behaviour.
How do I install Caddy?
The README gives the simplest cross-platform route: download Caddy from GitHub Releases and place the executable file in your PATH. It defers other install instructions to the online documentation at caddyserver.com/docs/install.
How do I use the Caddyfile?
The Caddyfile is Caddy's easy configuration format, and config adapters translate it into the native JSON config the server runs. The README points users to the Getting Started guide on the project website rather than reproducing a full example.
How do I use Caddy as a reverse proxy?
A reverse proxy is configured in Caddy's configuration format, and the README recommends the Getting Started guide on the project website for a working walkthrough. The README itself does not reproduce a reverse proxy example.
How do I install Caddy on Ubuntu?
The README does not give distribution-specific install commands. It says to download Caddy from GitHub Releases and place the executable in your PATH, and to see the online documentation for other install instructions.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/caddyserver-caddy)
Community notes