Caddy 2.11.6 hardens the reverse proxy by default and adds URLPattern matching
The 2.11.6 release caps request headers at 16 KiB, cuts Slowloris connections, and drops dot-header fields, at the cost of breaking changes. Read them before upgrading a production Caddy server.
October 1, 2026. Caddy ships 2.11.6, a maintenance release with a notable security hardening and a new request matcher built on the URLPattern standard. June 2026. The 2.11.4 release had already dropped header fields containing underscores, and 2.11.6 extends the same logic to dots. October 1, 2026. Go 1.26 becomes the minimum version for building Caddy and its plugins. Why it matters: the reverse proxy that does automatic HTTPS is changing several defaults — a 16 KiB header cap, one-minute idle timeouts, and dropped dot-headers — and you need to read them before upgrading a production server.
The reverse proxy that signs its own certificates
Caddy occupies a precise spot in self-hosting: it is the web server and reverse proxy that obtains and renews TLS certificates automatically, via ACME, with no manual setup and no separate certbot. For a homelab or a small team, it replaces the Nginx plus Let’s Encrypt assembly with a single Go binary, configured in a Caddyfile.
That simplicity has a flip side: much of Caddy’s security rests on the defaults the team chose. When those defaults change, as in 2.11.6, the change touches every server that never tuned anything — which is most of them.
The 2.11.6 release is presented by its maintainers as hardening, with an explicit warning: “most of the breaking changes come from security hardening, and most configs won’t notice.” But the ones that relied on the old behavior need to know.
Three hardening-by-default changes
The first change is the most visible: request headers are capped at 16 KiB by default. Before, Caddy used the Go default of 1 MB. A request with oversized headers — giant cookies, bloated tokens — now gets a 431 Request Header Fields Too Large. The use case is rare in practice, but real: apps that stuff a lot of state into cookies, or integrations that pass long tokens in headers. The documented escape hatch is the max_header_size server option.
The second is a direct answer to Slowloris, the attack that opens connections and lets them hang while sending a byte now and then. Caddy adds one-minute idle timeouts on reads and writes, reset on every successful read or write. A stalled connection gets cut; a slow one that is still making progress is left alone. Optional minimum transfer rates round out the device, and a timeouts directive allows per-route tuning. Pauses between writes — as with Server-Sent Events — do not count.
The third is the subtlest: header fields containing a dot are now dropped, exactly as underscores were in 2.11.4. The reason is documented: PHP folds dots into underscores, which could be used to impersonate legitimate headers. Needed dot-headers can be allowed explicitly through the expected_dot_headers server option.
A matcher that speaks the browser’s language
On the feature side, the most structural addition is the url_pattern matcher. It filters requests using URLPattern syntax, the WHATWG standard already used by browsers and many web frameworks. Named groups, wildcards, and regexp components are supported, and captured groups become {http.url_pattern.<component>.<group>} placeholders, with a matching CEL url_pattern function.
The point is to align Caddy’s routing language with the one developers already know from JavaScript. A matcher that resembles what you write in a service worker or a front-end framework reduces the friction between the reverse proxy and the application, and avoids inventing yet another syntax.
Two ergonomic options round out the release. tls_automate_names manages certificates for names without serving them in a site block — useful for preparing a domain before wiring it up. And expected_underscore_headers lets you explicitly keep the underscore headers that 2.11.4 dropped, if an application depended on them.
Reverse proxy, performance, and streams
The reverse proxy gets a series of concrete fixes. Partial responses are now flushed to clients properly, TCP half-close is propagated on upgraded streams, and versions 3 upstreams honor tls_trust_pool. Active health check state is kept separately per check configuration, so one handler’s failing probes no longer mark the upstream down for everyone else. The random_choose policy distributes correctly.
Server-Sent Events behind the encode directive now stream immediately instead of being buffered — a change that matters for real-time applications behind a compressing proxy. Graceful shutdown now waits for servers left over from previous configs, so long-lived responses started before a reload are not cut off at exit. And HTTP/3 now works over Tailscale and low-MTU links, thanks to a smaller initial QUIC packet size.
Performance improves too: fewer allocations in request hot paths, encoding negotiation, and the reverse proxy, with notably faster directory browsing for large directories.
A note on AI-assisted maintenance
The release notes carry an unusually candid aside about how the work gets done now. The maintainers thank contributors who “spent their LLM tokens responsibly,” and note that “AI has made contributions of all quality levels cheap and easy” — a double-edged reality for a project that now triages a flood of machine-generated patches alongside human ones. For a self-hoster reading the changelog, it is a small but honest signal that the project’s review burden has changed shape, even if the shipped code remains the same.
What to check before upgrading
The list of breaking changes is short, but each can bite. Go 1.26 becomes the minimum for building Caddy and its plugins — a point that mostly matters to those who build their own images or plugins. The 16 KiB header cap and the one-minute idle timeouts are default changes that can cut long-lived streams or clients with oversized headers. Dropping dot-headers can break an application that depends on them.
Two cases deserve particular attention. The first is mTLS inheritance: a wildcard site’s client_auth no longer applies to more specific hostnames that have their own site blocks. public.example.com no longer inherits mTLS from *.example.com. The second is import in named routes: the import directive now works inside named routes, and quoted braces are treated as literal arguments — enough to change the behavior of configs that relied on the old limit.
The best practice remains the same as for any upgrade: caddy validate on the config, a test on a non-critical environment, and a careful read of the breaking changes before touching production.
Verdict
If you run Caddy in production, this release is worth the upgrade for the hardening alone — the header cap and anti-Slowloris timeouts close real surfaces — but treat it as a version bump to verify, not a simple patch: caddy validate, then check max_header_size, expected_dot_headers, and client_auth inheritance if you use mTLS wildcards. If you sit behind Tailscale or on a low-MTU link, the HTTP/3 fix is one more argument. If you build your own plugins, plan for Go 1.26 before depending on a binary you will no longer be able to compile.