github caddyserver/caddy v2.11.6

4 hours ago

This patch release contains a large number of minor and some noticeable enhancements and bug fixes. Thank you to everyone who contributed or spent their LLM tokens responsibly to help with this release!

We have much more in the pipeline still, as AI has made contributions of all quality levels cheap and easy. We will be trying to go through them as quickly and efficiently as we can.

Huge thank you to our sponsors for keeping the project alive with resources, and for our maintainers who triage and assist tirelessly in this relentless new age of AI.

⚠️ Please read the breaking changes below before upgrading. Most of them come from security hardening, and most configs won't notice. If you were relying on one of the old behaviors, though, you'll want to know about it.

Highlights

  • New url_pattern request matcher: Match requests with the URLPattern standard, the same syntax used by browsers (JS) and many web frameworks. It supports named groups, wildcards, and regexp components. Captured groups become placeholders ({http.url_pattern.<component>.<group>}), and there's a matching url_pattern CEL function too. Thanks @dunglas! (#7787)
  • Slowloris mitigation: New idle read/write timeouts reset on every successful read or write. A stalled connection gets cut off, and a slow one that's still making progress is left alone. You can also set optional minimum transfer rates, and there's a new timeouts handler directive for per-route tuning. (#7913)
  • New tls_automate_names global option: Manage certificates for names without serving them in a site block. (#8015)
  • New expected_underscore_headers server option: If dropping header fields with underscores in 2.11.4 broke your app, you can now list the specific headers to keep. (#7809)
  • HTTP/3 over Tailscale and other low-MTU links now works, because the initial QUIC packet size is smaller. (#7886)
  • Reverse proxy got more love:
    • partial responses are flushed to clients properly (#7849)
    • TCP half-close is propagated on upgraded streams (#8027)
    • versions 3 upstreams now honor tls_trust_pool (#8042)
    • active health check state is kept separate per check config, so one handler's failing probes don't mark the upstream down for everyone else (#7916)
    • the random_choose policy distributes correctly now (#7873)
  • Server-sent events behind encode now stream immediately instead of being buffered. (#7905)
  • Graceful shutdown now waits for servers left over from previous configs, so long-lived responses that started before a reload aren't cut off at exit. (#8009)
  • Caddyfile: import now works inside named routes (#7986), and quoted braces are treated as literal arguments (#7875).
  • Logging:
  • Performance: fewer allocations in request hot paths, encoding negotiation, and the reverse proxy, from @jvoisin and @dunglas. Directory browsing is much faster for large directories. (#7847, #7903, #7911, #7925, #7926, #7936)
  • Other new stuff:
    • {http.request.proto_name} placeholder (#7782)
    • FastCGI populates SERVER_ADDR (#7912)
    • multiple authentication providers no longer clobber each other's responses (#7904)

⚠️ Breaking changes

  • Go 1.26 is now the minimum version for building Caddy and plugins. (#8056)
  • Request headers are limited to 16 KiB by default. Before, we used Go's 1 MB default. Requests with larger headers (giant cookies, oversized tokens, etc.) will now get 431 Request Header Fields Too Large. If you need more, raise it with the max_header_size server option.
  • New 1-minute idle read/write timeouts by default. If a request body read or a response write stalls (makes no progress at all) for longer than that, the connection is aborted. Pauses between writes, like with SSE, don't count. Some long-lived streams where the client goes quiet mid-body may be affected. You can tune these with read_body_idle and write_idle in the timeouts server option, or per-route with the timeouts directive. (#7913)
  • Request header fields containing . are now dropped, the same way underscores were in 2.11.4. PHP folds . to _, so these could be used to impersonate legitimate headers. If you need specific ones, allow them with the new expected_dot_headers server option.
  • A wildcard site's client_auth no longer applies to more specific hostnames that have their own site blocks. For example, public.example.com no longer inherits mTLS from *.example.com. If you were counting on that inheritance, configure client_auth on the specific site explicitly. (#7920)
  • Stricter config validation. Some configs that used to be silently accepted (and probably didn't do what you expected) are now errors:
    • duplicate named_routes (#7800)
    • duplicate forward_auth uri (#7814)
    • invalid weighted_round_robin weights (#7807)
    • a non-integer browse file_limit (#7988)
    • duplicate or ambiguous map inputs (#8067)
    • malformed map destination placeholders (#8074)
    • module paths with ambiguous @ version separators (#7974)
  • Admin API /load now returns 400 with warnings inside a valid JSON body when a config is invalid. Before, it returned 200 with two concatenated JSON objects. (#7267)
  • method matcher values are normalized to uppercase, so method get now matches GET requests. (#7832)

Security fixes

Thank you to everyone who reported responsibly and helped with patches:

  • reverseproxy: When a route used both forward_auth and reverse_proxy, a request could be sent on the wrong upstream connection. Reported by @carlt, fixed by @WeidiDeng. (GHSA-6365-7ppr-5r92, #7859)
  • reverseproxy: Hop-by-hop headers from upstreams are now stripped from 101 Switching Protocols responses too. Thanks @jirn073-76.
  • caddyhttp: handle_path and uri strip_prefix/strip_suffix now canonicalize the resulting path, so it can't bypass path-based authorization. Thanks @steadytao.
  • caddyhttp: Extended the 2.11.4 header-alias filter to . (see above) to prevent bypassing forward_auth copy_headers with PHP/FastCGI backends. Thanks @dunglas.
  • caddyhttp: The path_regexp matcher now normalizes Windows backslashes like the path matcher does. This completes the fix for CVE-2026-52844. Thanks @thientd. (#7858)
  • fileserver: Windows 8.3 short names are rejected in every path component, not just the last one. Thanks @DavidCarliez. (#7952)
  • fileserver: Fixed ETag collisions between files with different modification times and sizes. Thanks @dunglas.
  • fastcgi: The client's Proxy header is no longer passed to backends as HTTP_PROXY (HTTPoxy). Thanks @bzyy1024. (#7934)
  • reverseproxy: Sticky session cookie hashes are compared in constant time. Thanks @alhudz. (#7853)
  • admin: Request paths are normalized in the remote admin access check, and origin/host allow-lists compare case-insensitively (defense-in-depth). Thanks @AmariahAK, @mohammed90, and @hktitof. (#7910, #7973, #7993)
  • caddyhttp: Oversized request bodies used through placeholders now correctly return 413. Thanks @hktitof. (#7969)

⚠️ These security patches may be breaking if your application relies on the buggy behaviors.

🚨 Notice for Caddy plugin maintainers: Dependabot will probably alert you to the security fixes in Caddy and urge you to upgrade it in your go.mod file. Please ONLY upgrade the Caddy dependency if there's a change to an exported API your plugin uses. Note that doing so now also requires Go 1.26.

Thank you to everyone who was involved this release, especially our 40 new contributors! 🎉

What's Changed

  • reverseproxy: replace placeholders specified for sni while using http3 by @WeidiDeng in #7737
  • caddyhttp: add {http.request.proto_name} placeholder for spec-compliant protocol names by @Jualhosting in #7782
  • httpcaddyfile: error on duplicate named_routes by @vijayvenkatj in #7800
  • cmd: colored error message in WrapCommandFuncForCobra (#7760) by @u5surf in #7768
  • chore: add missing "is" in SECURITY.md by @AliMickey in #7802
  • reverseproxy: validate on weighted_round_robin loadbalancing policy by @vijayvenkatj in #7807
  • forwardauth: error on duplicate uri subdirective by @vijayvenkatj in #7814
  • encode: add standard benchmark and conformance harness by @ottenhoff in #7804
  • caddyhttp: restore allow_underscore_in_headers server option by @bluegate-studio in #7809
  • rewrite: fix wrong index check in trimPathPrefix by @alhudz in #7812
  • intercept: fix replace_status being silently dropped by @oksusucha in #7810
  • fileserver: append repeated hide subdirectives instead of overwriting by @luccinmasirika in #7817
  • rewrite: scope keyed query replace to its named key by @alhudz in #7818
  • reverseproxy: log status 499 instead of 0 when client disconnects by @larrasket in #7827
  • tracing: fix BatchSpanProcessor goroutine leak on config reload by @Dean2026 in #7826
  • caddyhttp: normalize method names to uppercase in MatchMethod.Provision by @yintaisha in #7832
  • caddyhttp: add URL pattern request matcher by @dunglas in #7787
  • caddyhttp: fix escaped path matcher over-matching longer paths by @alhudz in #7828
  • core: preserve metrics registry in Context.WithValue by @dunglas in #7861
  • reverseproxy: compare sticky-session cookie hash in constant time by @alhudz in #7853
  • reverseproxy: fix misleading handle_response error for extra matcher args by @TowyTowy in #7869
  • reverseproxy: save dial info in a context key instead of a variable k… by @WeidiDeng in #7859
  • caddyhttp: fix path_regexp (MatchPathRE) Windows backslash bypass by @thientd in #7858
  • intercept: fix misleading handle_response error for extra matcher args by @TowyTowy in #7871
  • build(deps): bump cel-go from v0.28.1 to v0.29.2 by @techknowlogick in #7872
  • reverseproxy: fix broken reservoir sampling in random_choose policy by @TowyTowy in #7873
  • caddyfile: treat quoted braces as literal arguments by @elee1766 in #7875
  • logging: hash query parameter values in QueryFilter by @PichuChen in #7884
  • chore: fix sabotage spelling in nolint comments by @futurehua in #7892
  • pki: Handle error immediately after reading root from disk by @hslatman in #7896
  • log: support d (day) in roll_interval directive by @mohammed90 in #7900
  • deps: update GitHub Actions and Go modules by @steadytao in #7876
  • build(deps): bump google.golang.org/grpc from 1.81.1 to 1.82.1 by @dependabot[bot] in #7908
  • core: reduce QUIC initial packet size for low-MTU paths by @Salynn in #7886
  • rewrite: preserve non-canonical path encoding after uri replace by @larrasket in #7907
  • caddyhttp: use canonical header key casing to avoid re-canonicalization by @dunglas in #7911
  • caddyconfig: Register nested named-route invokes transitively by @SillyZir in #7898
  • encode: reduce allocations in AcceptedEncodings by @jvoisin in #7847
  • encode: flush headers immediately for server-sent events responses by @SillyZir in #7905
  • fileserver: speed up directory browsing for large directories by @firefart in #7903
  • caddyauth: isolate provider responses to prevent cross-provider clobbering by @SillyZir in #7904
  • log: don't allow overwriting singly-assigned vals by @mohammed90 in #7927
  • caddyconfig: cancel HTTP loader requests with context by @cuishuang in #7918
  • feat(fastcgi): populate SERVER_ADDR by default by @renich in #7912
  • reverseproxy: reduce allocation/CPU overhead in request hot paths by @jvoisin in #7925
  • reverseproxy: preallocate upstream slices with known sizes by @jvoisin in #7926
  • network_proxy: reject proxy URLs that resolve to a port with no host by @r0h1tb in #7922
  • caddyhttp: log recovered handler panics at ERROR level by @ousamabenyounes in #7924
  • caddyhttp: allocate the request UUID lazily instead of on every request by @jvoisin in #7936
  • caddyhttp: shield specific hostnames from a covering wildcard's client auth by @SillyZir in #7920
  • reverseproxy: isolate active health-check state per distinct check config by @SillyZir in #7916
  • logging: add set_cookie log filter for Set-Cookie response headers by @steffenbusch in #7888
  • caddyhttp: fix url_pattern authorization bypass via encoded-slash traversal by @dunglas in #7941
  • fix: close resources on error paths by @ittakestwo123 in #7940
  • fastcgi: fix HTTPoxy vulnerability by @bzyy1024 in #7934
  • caddyhttp: mitigate slowloris via idle read/write deadlines by @dunglas in #7913
  • chore: fix lint errors from newer golangci-lint by @faiyazrahmann in #7958
  • pki: honor skip_install_trust for explicit tls internal issuers by @gautamrizwani in #7894
  • caddyfile: clarify ArgErr documentation by @0jaspahwa in #7960
  • rewrite: fix URI splitting when a query or fragment arrives via a placeholder by @francislavoie in #7947
  • admin: normalize request path in remote admin access-control check (defense-in-depth) by @AmariahAK in #7910
  • fileserver: reject short names in every path component by @DavidCarliez in #7952
  • rewrite: fix strip_path_suffix ignoring percent-encoding by @TowyTowy in #7877
  • admin: stabilise log redaction test by @steadytao in #7942
  • chore: fix canonicalheader failures by @steadytao in #7964
  • cmd: upgrade automemlimit to v1.0.0 by @dunglas in #7978
  • caddy: fix ParseNetworkAddress port-range span off-by-one by @mohammed90 in #7975
  • usagepool: avoid lock inversion after constructor failure by @kojah in #7968
  • cmd: reject ambiguous module version separators by @mohammed90 in #7974
  • admin: fix origin allow-list host comparison to be case-insensitive by @mohammed90 in #7973
  • caddyfile: fix importGraph self-loop and stale-edge bugs by @mohammed90 in #7971
  • caddyhttp: remove unused QUICConfig from the HTTP/3 server by @faiyazrahmann in #7980
  • fileserver: reject non-integer browse file_limit by @SashaMIT in #7988
  • caddyhttp: surface write timeout errors in access log by @faiyazrahmann in #7945
  • admin: fix host allow-list comparison to be case-insensitive by @hktitof in #7993
  • caddytls: synchronize storage cleaner with TLS.Stop via context and WaitGroup by @jum in #7954
  • httpcaddyfile: give each adaptation its own directive order by @faiyazrahmann in #7995
  • listen: don't wedge a reloaded listener sharing a socket on Windows by @alexandre-daubois in #7999
  • caddyhttp: demote Alt-Svc ErrNoAltSvcPort to debug level by @jum in #8000
  • events: skip dispatch setup when nothing is subscribed by @u5surf in #7997
  • reverseproxy: replace only known placeholders in health check body by @IslamElsayed in #8003
  • acmeserver: say when the CA database is locked by another process by @faiyazrahmann in #8007
  • fix: require module path boundaries when matching packages by @wangjingshuiku in #7957
  • caddyhttp: preserve unknown placeholders in static response headers by @hktitof in #8014
  • build(deps): bump google.golang.org/grpc from 1.82.1 to 1.83.1 by @dependabot[bot] in #7984
  • reverseproxy: propagate TCP half-close on upgraded streams by @btncwn in #8027
  • build(deps): bump google.golang.org/grpc from 1.83.1 to 1.83.2 by @dependabot[bot] in #8028
  • caddyfile: Expand imports inside named routes by @XiaoleC05 in #7986
  • chore(deps): bump cel-go and switch import to new url by @techknowlogick in #8030
  • core: synchronize Stop with concurrent config reloads by @H-XX-D in #8038
  • reverse_proxy with versions 3 silently ignores tls_trust_pool and verifies against the system roots by @tippexs in #8042
  • admin: fix warnings and status code in /load API by @amirdaraby in #7267
  • fastcgi: explain 411 responses for unknown-length bodies by @Rohilalala in #7956
  • Move websocket header normalization later by @nfreya in #7921
  • chore: remove AI moderator workflow by @mohammed90 in #8059
  • caddyhttp: surface 413 for oversized request body placeholders by @hktitof in #7969
  • reverseproxy: Fix partial response not flushed to clients by @WeidiDeng in #7849
  • caddyhttp: fix randString sameCase dictionary to match its docs by @mohammed90 in #7972
  • requestbody: replace only known placeholders in the set body by @hktitof in #8008
  • internal: fix MaxSizeSubjectsListForLog off-by-one when maxToDisplay is 0 by @mohammed90 in #7970
  • caddyauth: only replace known placeholders in basic auth credentials by @hktitof in #8017
  • map: Distinguish duplicate literal and regexp inputs by @Indra55 in #8067
  • map: reject malformed destination placeholders by @sleet0922 in #8074
  • httpcaddyfile: new tls_automate_names global option by @IslamElsayed in #8015
  • build: bump all dependencies - (Go 1.26 floor) by @steadytao in #8056
  • caddyhttp: wait for servers from previous configs on exit by @dunglas in #8009
  • caddyhttp: use errors.AsType for request body limit errors by @mholt in #8090
  • ci: pin cosign to v2 for release signing by @francislavoie in #8092

New Contributors

Full Changelog: v2.11.4...v2.11.6

Don't miss a new caddy release

NewReleases is sending notifications on new releases.