Caddy 2.11.6 → 2.11.8
2 releases · 13 breaking changes · 9 security fixes · 5 advisories fixed
9security13breaking0deprecated32feature1fix
Vulnerabilities fixed by this upgrade
- 5.9 GHSA-f278-mgc7-46j3 php_fastcgi authorization bypass via dot-segments in the request path
- medium GHSA-jf74-f69g-hcj9 file_server exposes private keys when the data directory falls back to ./caddy
- medium GHSA-69hv-5jfc-mvmv rewrite re-decodes {http.matchers.file.remainder}, bypassing path-based deny rules
- medium GHSA-25cw-38mq-q7m6 file matcher resolves /file/./ to a regular file, bypassing suffix path rules
- high GHSA-r766-rf9f-588h Host and :authority values with dot segments or slashes escape a {host}-based root
Breaking changes
- breaking Requests with an invalid or dots-only host (like ..) are rejected with 400 on every protocol, before any route runs. Before, HTTP/2 and HTTP/3 also accepted hosts containing / or . (#8328) in 2.11.8
- breaking request_body max_size now rejects a request with 413 as soon as its Content-Length is over the limit, even if nothing would have read the body. (#8337) in 2.11.8
- breaking php_fastcgi sites without an index.php: requests for paths that don't exist are now sent to PHP as index.php, as the documented first_exist_fallback try policy always promised, instead of falling through to file_server and returning 404. Also, PATH_INFO and DOCUMENT_URI are now derived from the… in 2.11.8
- breaking A try_files / file candidate ending in / only matches a directory, so /foo.txt/ no longer matches the file /foo.txt. (#8326) in 2.11.8
- breaking encode now compresses responses even when the request has Cache-Control: no-transform (a response's no-transform is still honored), and responses that could have been encoded always get Vary: Accept-Encoding. To keep the old behavior: @transformable not header Cache-Control no-transform and encode… in 2.11.8
- breaking Stricter config validation. Some configs that were silently accepted (and didn't do what you'd expect) are now errors: in 2.11.8
- breaking more than one expression line in the same matcher set (combine them with &&) (#8183) in 2.11.8
- breaking unknown TLS curves or protocol_min / protocol_max names, like TLS1.3 instead of tls1.3, which used to be silently ignored (#8266) in 2.11.8
- breaking a repeated sni_regexp in a TLS policy match block (#8137) in 2.11.8
- breaking ip_mask log filter sizes out of range, like 33 for IPv4 (#8241) in 2.11.8
- breaking basic_auth usernames that end up identical after placeholder expansion (#8315) in 2.11.8
- breaking ports longer than 11 characters (#8277) in 2.11.8
- breaking Evaluation order now follows the Caddyfile for handle_errors blocks with status codes and for vars with non-path matchers. If you had overlapping codes or set the same variable twice, a different block or value may now win. (#8195, #8197) in 2.11.8
Security changes
- security caddyhttp: A Host / :authority value with dot segments or slashes could escape the intended directory when used in file paths, like root /srv/sites/{host}, and with php_fastcgi even execute files outside it. Such hosts are now rejected (see above). Thanks @dunglas. (GHSA-r766-rf9f-588h, #8328) in 2.11.8
- security fastcgi: SCRIPT_FILENAME is now derived from the same normalized path that path matchers see, so a route protected by a path matcher can't be made to run a different script, e.g. with /admin.php/../../other.php. The php_fastcgi rewrite also always runs now (see above). Thanks @pamnario.… in 2.11.8
- security fileserver: The file matcher no longer resolves paths like /.env/./ to the regular file, which bypassed path rules such as path *.env. Thanks @dunglas. (GHSA-25cw-38mq-q7m6, #8326) in 2.11.8
- security rewrite: {http.matchers.file.remainder} is now escaped before rewriting, so double-encoded path info (like ..%252f) can't turn into ../ after path rules have run. Thanks @dunglas. (GHSA-69hv-5jfc-mvmv, #8325) in 2.11.8
- security fileserver: When neither HOME nor XDG_DATA_HOME is set, Caddy's data directory falls back to ./caddy, and a file_server serving the working directory could expose certificates and private keys. That directory is now always hidden. Thanks @dunglas. (GHSA-jf74-f69g-hcj9, #8327) in 2.11.8
- security fastcgi: The php_fastcgi trailing-slash redirect for directories with an index.php can no longer be turned into an open redirect to another site. Thanks @sam-kota. (#8285) in 2.11.8
- security fileserver: Files excluded with hide no longer show up in browse listings, and hidden precompressed sidecars are no longer served. Thanks @agammann. (#8155) in 2.11.8
- security reverseproxy: Hop-by-hop headers are stripped from 1xx responses and from copy_response_headers in handle_response, completing the fix for CVE-2026-55458. Thanks @SssiiiSssii1 and @tomholford. (GHSA-8qx7-j2v2-x6q5, #8256, #8270) in 2.11.8
- security logging: The query log filter now parses request URIs like the server does, so parameters after a # are redacted too. Thanks @jayhemnani9910. (#8255) in 2.11.8
Every release in between
featureIdle timeouts, round two. More fixes for the idle read/write timeouts introduced in 2.11.6:
featurePer-route timeouts now take precedence over the server-wide read_body_idle / write_idle, and nested timeouts use the innermost one. (#8121)
featureOver HTTP/2, a handler that paused reading the request body (like reverse_proxy uploading to a slow upstream) no longer loses the body and fails with 502. (#8125)
featureDeadlines set by handlers now act as a ceiling instead of being overridden, so things like Mercure's dispatch_timeout and FrankenPHP's request_body_timeout work again. Thanks @dunglas! (#8145, #8284)
featureHTTP/1.1 uploads that time out with a little body left no longer hang instead of sending the error response. Thanks @SiyXo511! (#8178)
featureBinding privileged ports with fewer capabilities (Linux): If CAP_NET_BIND_SERVICE is permitted but not effective, Caddy raises it just for binding a privileged port. Packagers can use setcap cap_net_bind_service=+p instead of +ep, so Caddy still starts under --cap-drop ALL, OpenShift's…
featurenew log_level directive to set the access log level for matching requests, e.g. log_level @healthchecks DEBUG (#8201)
featurenew {time.now.rfc3339} placeholder. Thanks @steadytao! (#8140)
featurenew not matcher for TLS connection policies. Thanks @simonether! (#8138)
featurethe tls matcher can be used in CEL expression matchers: tls(), tls('complete'), tls('early_data'). Thanks @stackedbyaradhya! (#8088)
featureheaders you set yourself (like Strict-Transport-Security via header) are no longer lost when the upstream sends a 1xx response first, like 103 Early Hints (#8169)
featurerequest trailer values are forwarded, not just their names. Thanks @krishsinghhura! (#8246)
featurerequest-only expressions in lb_retry_match no longer retry successful responses (a regression since 2.11.3) (#8168)
featuredial errors through a forward proxy and to FastCGI backends are retried like other dial errors. Thanks @adarshxsh and @zemamba! (#8264, #8172)
featureweighted_round_robin no longer panics with fewer weights than upstreams, and no longer picks an unavailable upstream when there's only one weight; random_choose picks the least loaded host when all are busy (#8159, #8320, #8160)
featurewith request_buffers / response_buffers unlimited, a body that fails partway is no longer treated as complete (#8207)
featureconnections and sockets are no longer leaked when a protocol upgrade (e.g. WebSocket) fails during setup, or when a handle_response status isn't a number. Thanks @Version2beta and @BenMacha! (#8269, #8318)
featureHTTP/3 client cancellations are logged at debug level instead of as warnings. Thanks @larrasket! (#8135)
breakingRequests with an invalid or dots-only host (like ..) are rejected with 400 on every protocol, before any route runs. Before, HTTP/2 and HTTP/3 also accepted hosts containing / or . (#8328)
breakingrequest_body max_size now rejects a request with 413 as soon as its Content-Length is over the limit, even if nothing would have read the body. (#8337)
breakingphp_fastcgi sites without an index.php: requests for paths that don't exist are now sent to PHP as index.php, as the documented first_exist_fallback try policy always promised, instead of falling through to file_server and returning 404. Also, PATH_INFO and DOCUMENT_URI are now derived from the…
breakingA try_files / file candidate ending in / only matches a directory, so /foo.txt/ no longer matches the file /foo.txt. (#8326)
breakingencode now compresses responses even when the request has Cache-Control: no-transform (a response's no-transform is still honored), and responses that could have been encoded always get Vary: Accept-Encoding. To keep the old behavior: @transformable not header Cache-Control no-transform and encode…
breakingStricter config validation. Some configs that were silently accepted (and didn't do what you'd expect) are now errors:
breakingmore than one expression line in the same matcher set (combine them with &&) (#8183)
breakingunknown TLS curves or protocol_min / protocol_max names, like TLS1.3 instead of tls1.3, which used to be silently ignored (#8266)
breakinga repeated sni_regexp in a TLS policy match block (#8137)
breakingip_mask log filter sizes out of range, like 33 for IPv4 (#8241)
breakingbasic_auth usernames that end up identical after placeholder expansion (#8315)
breakingports longer than 11 characters (#8277)
breakingEvaluation order now follows the Caddyfile for handle_errors blocks with status codes and for vars with non-path matchers. If you had overlapping codes or set the same variable twice, a different block or value may now win. (#8195, #8197)
securitycaddyhttp: A Host / :authority value with dot segments or slashes could escape the intended directory when used in file paths, like root /srv/sites/{host}, and with php_fastcgi even execute files outside it. Such hosts are now rejected (see above). Thanks @dunglas. (GHSA-r766-rf9f-588h, #8328)
securityfastcgi: SCRIPT_FILENAME is now derived from the same normalized path that path matchers see, so a route protected by a path matcher can't be made to run a different script, e.g. with /admin.php/../../other.php. The php_fastcgi rewrite also always runs now (see above). Thanks @pamnario.…
securityfileserver: The file matcher no longer resolves paths like /.env/./ to the regular file, which bypassed path rules such as path *.env. Thanks @dunglas. (GHSA-25cw-38mq-q7m6, #8326)
securityrewrite: {http.matchers.file.remainder} is now escaped before rewriting, so double-encoded path info (like ..%252f) can't turn into ../ after path rules have run. Thanks @dunglas. (GHSA-69hv-5jfc-mvmv, #8325)
securityfileserver: When neither HOME nor XDG_DATA_HOME is set, Caddy's data directory falls back to ./caddy, and a file_server serving the working directory could expose certificates and private keys. That directory is now always hidden. Thanks @dunglas. (GHSA-jf74-f69g-hcj9, #8327)
securityfastcgi: The php_fastcgi trailing-slash redirect for directories with an index.php can no longer be turned into an open redirect to another site. Thanks @sam-kota. (#8285)
securityfileserver: Files excluded with hide no longer show up in browse listings, and hidden precompressed sidecars are no longer served. Thanks @agammann. (#8155)
securityreverseproxy: Hop-by-hop headers are stripped from 1xx responses and from copy_response_headers in handle_response, completing the fix for CVE-2026-55458. Thanks @SssiiiSssii1 and @tomholford. (GHSA-8qx7-j2v2-x6q5, #8256, #8270)
securitylogging: The query log filter now parses request URIs like the server does, so parameters after a # are redacted too. Thanks @jayhemnani9910. (#8255)
+109 more items in the release notesfeatureFixed: crash and dropped streams caused by the new idle timeouts. 2.11.6 introduced default idle read/write timeouts, which caused some problems:
featureIn 2.11.6, the request body's idle deadline could outlive the handler that set it: Both are fixed in #8107. Thanks @steadytao!
featureOver HTTP/2, Caddy could panic with a nil pointer dereference when the reverse proxy was still reading a request body after the handler had returned. (#8101)
featureOver HTTP/1.1, streaming responses to requests with a body, such as SSE clients that open the stream with a POST, were cut off exactly 60 seconds after the body was read. (#8103)
featureOver HTTP/2, streaming responses that paused between writes for longer than write_idle (1 minute by default), like quiet SSE streams, were reset with a stream error. As documented, only a write that stalls should count. Thanks @WeidiDeng! (#8118, #8119)
featureFixed: placeholders for missing cookies are empty again. Since 2.11.6, places that keep unknown placeholders as written, like respond headers, would output {http.request.cookie.} literally when the cookie wasn't in the request. The same happened to {http.request.tls.} on plain HTTP requests. Both…
featureNew: support for the Incremental header field (RFC 10036). It's the standard replacement for NGINX's proprietary X-Accel-Buffering header. If an upstream response has Incremental: ?1, reverse_proxy forwards it immediately, the same as flush_interval -1, and encode streams it instead of holding it…
featureIf the request_buffers or response_buffers options would prevent incremental forwarding, Caddy responds with 501 Not Implemented instead of silently buffering, as the RFC requires.
featureThe new proxy_status_name option adds a Proxy-Status header to those responses, explaining why the message was refused.
featureFaster TLS handshakes: When nothing subscribes to certificate events and debug logging is off, CertMagic no longer builds event data for every handshake. Certificate lookup per handshake is about twice as fast, with 10 allocations instead of 15. Thanks @u5surf! (#8010)
featureUnix sockets: When a reload moves a listener (or the admin endpoint) off a Unix socket, the old socket now closes right away and its file is removed. Before, clients connecting to the old path would hang until the next garbage collection, about 2 minutes later. Thanks @littfed! (#8061)
featureHeaders handler: Multiple Set-Cookie values in a JSON config's set are now sent as separate header fields, instead of being joined with commas into one field that clients can't parse. Thanks @Indra55! (#8080)
featurecaddy fmt no longer deletes an opening brace at the very end of the input. Thanks @n0liu! (#8047)
otherevents: tell CertMagic which events are worth emitting
featurefeat: implement the Incremental header field (RFC 10036)
othercaddyfile: Keep an opening brace that ends the input
otherlisteners: close unix socket immediately and unlink file on reload
otherreverseproxy: stabilise half-close test
othercaddyhttp: keep absent optional placeholders empty
otherheaders: Preserve separate Set-Cookie values
othercaddyhttp: end request-body deadline ownership with handler
fixtimeouts: fix idle writer terminate h2 response writers between writes