Skip to content
senn-techsenn-tech
Back to blog
Security
Security and data protection2026-11-08· By Franz Senn

Caddy 2.11.8 and 2.11.9: five flaws around file serving and PHP, and why a plain reverse proxy does not have them

Caddy released version 2.11.8 on 4 November and 2.11.9 a day later. Both carry security fixes. Five of them are GitHub advisories dated 4 November, all fixed in 2.11.8 and valid for 2.11.7 and older. None has a CVE number. The advisory pages rate one as high and four as moderate.

The five advisories and their preconditions

Each flaw needs a particular directive in the Caddyfile. That decides whether you are affected. The details below come from the advisory pages on GitHub, which we read on 8 November.

AdvisoryRatingPrecondition
GHSA-r766-rf9f-588hhighA root built from {host}, for example root /srv/sites/{host}, on a catch-all site or with on-demand TLS, plus file_server or php_fastcgi (FrankenPHP too, per the advisory). Sites that list their host names are not affected. No login needed.
GHSA-f278-mgc7-46j3moderate (CVSS 5.9)php_fastcgi or a FastCGI transport, with no index.php (and no index sub-directive) in the site root, and an access rule (basic_auth or handle) protecting a PHP path: the flaw bypasses exactly that rule. The attacker must know the name of an existing PHP file. Sites with an index.php are not affected, per the advisory.
GHSA-25cw-38mq-q7m6moderatetry_files {path} together with file_server and a path deny rule before it, such as path *.env. php_fastcgi and FrankenPHP php_server ship this try_files route themselves, per the advisory.
GHSA-69hv-5jfc-mvmvmoderatetry_files with split_path and a rewrite that uses {http.matchers.file.remainder} (FrankenPHP php_server generates it), Bypassing a deny rule needs such a rule placed before the rewrite; manipulating the query string or truncating the path needs only the rewrite.
GHSA-jf74-f69g-hcj9moderatefile_server serves the working directory, and neither HOME nor XDG_DATA_HOME is set. The command-line call caddy file-server is affected as well.

Two more advisories appear in the 2.11.9 release notes, and we could not read them: GHSA-q34h-28rj-5hjm (client headers override the FastCGI environment) and GHSA-9cr8-wmhh-39pm (Windows short names and alternate data streams). Neither is published yet as of 8 November: the advisory page returned a 404. We can say nothing about rating or affected range, only the release-note text exists.

CVE-2026-55458 is not one of the five. Its advisory, GHSA-8qx7-j2v2-x6q5, is rated low and dated 13 October: hop-by-hop headers leak out of WebSocket responses (status 101) through reverse_proxy. The advisory says it applies up to 2.11.4 and is fixed from 2.11.5; the 2.11.8 release notes list it as well.

What changes with the update

Version 2.11.8 answers with status 400 for host names that are invalid or consist only of dots. With php_fastcgi and no index.php, missing paths route to index.php, and try_files candidates ending in a slash now match directories only. Configuration validation is stricter.

Version 2.11.9 is a hotfix for a regression from 2.11.8: requests for directories with a trailing slash skipped index.php, for example /wp-admin/ on WordPress. Three more changes come with it. Conflicting client_auth settings across site blocks are now an error. FastCGI answers 400 when the script selection is ambiguous. And FastCGI environment values now override client HTTP_* headers of the same name. This list comes from the release pages as read on 8 November.

What our gateway Caddyfile shows

On 8 November we searched the Caddyfile of our homelab Caddy gateway (the reverse proxy in front of the internal web services) for the directives, one grep -c <directive> Caddyfile per directive. The result: file_server 0, try_files 0, php_fastcgi 0, root 0, on_demand 0. The placeholder {host} appears 8 times, none of them in a root. The precondition of all five advisories is missing on that gateway. This is an inference from the configuration, not an attack attempt.

Servers that deliver pages themselves are different. Anyone running Caddy with php_fastcgi checks GHSA-f278, but only if the site root has no index.php and an access rule protects a PHP path. The advisory states explicitly that sites with an index.php or the index sub-directive are not affected, and says front-controller applications such as WordPress, Laravel and Symfony are masked because the rewrite runs there. With try_files or FrankenPHP php_server, GHSA-25cw and GHSA-69hv are the checks. Anyone serving several customer sites from one catch-all block should look first for a root with {host}. The check takes a minute.

What to do now

  1. Run caddy version on every host. Anything below 2.11.9 is due for an update.
  2. Run grep -n -E "file_server|php_fastcgi|php_server|try_files|^\s*root |on_demand" Caddyfile. No hit means the precondition of the five advisories is missing from the Caddyfile. Calls of caddy file-server (GHSA-jf74) appear in no Caddyfile; look for them in systemd units, scripts and container definitions.
  3. If there are hits: a root containing {host} makes GHSA-r766 the most urgent check, because it needs no login.
  4. Read the breaking changes before updating. A test against a copy of the Caddyfile shows whether the stricter validation rejects anything: caddy validate --config Caddyfile.

For a Caddy in a container without an admin endpoint the usual rule applies: the configuration cannot be reloaded, and the update means restarting the container.

How we block access at the gateway is in the post Intrusion Prevention: CrowdSec instead of fail2ban. The reverse proxy itself is described in Reverse Proxy with Caddy: HTTPS by Default.

Further Reading

Primary sources

  • Caddy, release v2.11.8, 4 November 2026: github.com
  • Caddy, release v2.11.9, 5 November 2026: github.com
  • Caddy, list of security advisories: github.com
  • Caddy, advisory GHSA-r766-rf9f-588h, 4 November 2026: github.com
  • Caddy, advisory GHSA-f278-mgc7-46j3, 4 November 2026: github.com
  • Caddy, advisory GHSA-25cw-38mq-q7m6, 4 November 2026: github.com
  • Caddy, advisory GHSA-69hv-5jfc-mvmv, 4 November 2026: github.com
  • Caddy, advisory GHSA-jf74-f69g-hcj9, 4 November 2026: github.com
  • Caddy, advisory GHSA-8qx7-j2v2-x6q5, 13 October 2026, CVE-2026-55458: github.com

Our own measurement

  • The directive counts in the Caddyfile of the homelab Caddy gateway are from 8 November 2026 (grep -c per directive). GHSA-q34h and GHSA-9cr8 were not yet published that day (404); only the release-note text was available.
Questions?
Is a Caddy that only does reverse_proxy affected by these flaws?+

Going by the preconditions in the advisories, no. All five need file_server, php_fastcgi, FrankenPHP php_server, try_files with a rewrite or a root built from {host} (the command-line call caddy file-server counts as file_server). That is an inference from the preconditions, not a test with an attack. CVE-2026-55458 is the exception, because it also touches reverse_proxy (low severity, see below).

Why move to 2.11.9 anyway?+

Version 2.11.9 fixes a regression from 2.11.8 and hardens FastCGI further. If you skip 2.11.8 you skip the regression. There are also breaking changes to test before the update.

How do I check my Caddyfile in a minute?+

Run grep for the directives file_server, php_fastcgi, php_server, try_files and root in the Caddyfile, and look for calls of caddy file-server. No hit means the precondition is missing. If there are hits, check whether a root contains a placeholder such as {host}.