Advisories for Golang/Github.com/Caddyserver/Caddy package

2026

Caddy: stripHTML template function bypass

Caddy’s stripHTML template function cannot reliably remove all HTML tags from input strings. Certain malformed HTML, such as <<>img src=x onerror=alert()>, can bypass the tag-stripping logic, potentially leaving dangerous content in the output if it is later rendered as HTML. This may allow client-side XSS in cases where untrusted strings are rendered unsafely.

Caddy: stripHTML template function bypass

Caddy’s stripHTML template function cannot reliably remove all HTML tags from input strings. Certain malformed HTML, such as <<>img src=x onerror=alert()>, can bypass the tag-stripping logic, potentially leaving dangerous content in the output if it is later rendered as HTML. This may allow client-side XSS in cases where untrusted strings are rendered unsafely.

Caddy: FastCGI header normalization bypass in `forward_auth copy_headers`

forward_auth copy_headers deletes the exact client-supplied identity header before copying the trusted value from the auth gateway. But when the request later goes through php_fastcgi, Caddy normalizes HTTP headers into CGI variables by replacing - with _. This lets a client send an underscore alias that survives the forward_auth delete step but becomes the same PHP/FastCGI variable: Remote-Groups -> HTTP_REMOTE_GROUPS Remote_Groups -> HTTP_REMOTE_GROUPS Remote-User -> HTTP_REMOTE_USER Remote_User -> HTTP_REMOTE_USER Result: …

Caddy: FastCGI header normalization bypass in `forward_auth copy_headers`

forward_auth copy_headers deletes the exact client-supplied identity header before copying the trusted value from the auth gateway. But when the request later goes through php_fastcgi, Caddy normalizes HTTP headers into CGI variables by replacing - with _. This lets a client send an underscore alias that survives the forward_auth delete step but becomes the same PHP/FastCGI variable: Remote-Groups -> HTTP_REMOTE_GROUPS Remote_Groups -> HTTP_REMOTE_GROUPS Remote-User -> HTTP_REMOTE_USER Remote_User -> HTTP_REMOTE_USER Result: …

Caddy: Remote Admin Authorization Bypass on PKI Endpoints via Prefix-Based Path Matching

Caddy's remote admin access control performs path authorization using prefix matching: admin.go: strings.HasPrefix(r.URL.Path, allowedPath) This allows a client certificate authorized only for /pki/ca/prod to access sibling PKI resources whose paths merely share the same prefix, such as /pki/ca/prod-backup. This is an authorization bug in Caddy's source code, not a misconfiguration issue. The configured policy is more restrictive than the behavior that Caddy actually enforces.

Caddy: Remote Admin Authorization Bypass in `/config` API via Array Index Normalization

A remote admin client certificate restricted to the following path: /config/apps/http/servers/srv/routes/0 can still read and modify a different array element by requesting: /config/apps/http/servers/srv/routes/01 This happens because: the authorization layer uses string prefix matching the /config traversal layer parses array indices numerically using strconv.Atoi() So: authorization sees /…/01 as matching /…/0 traversal resolves 01 to numeric index 1 the request therefore targets routes[1], not routes[0] This is not just a prefix-match …

Caddy: Unsafe Unicode Handling in FastCGI splitPos Allows Execution of Non-PHP Files

The FastCGI transport's splitPos() in modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go misuses golang.org/x/text/search with search.IgnoreCase when the request path contains a non-ASCII byte. Two distinct flaws in that fallback let an attacker mislead Caddy's FastCGI splitting into treating a non-.php (or other configured split_path extension) file as a script. In any deployment where the attacker can place content into a file served via FastCGI (uploads, file storage, etc.), this can be escalated to remote code …

Caddy's vars_regexp double-expands user input, leaking env vars and files

The vars_regexp matcher in vars.go:337 double-expands user-controlled input through the Caddy replacer. When vars_regexp matches against a placeholder like {http.request.header.X-Input}, the header value gets resolved once (expected), then passed through repl.ReplaceAll() again (the bug). This means an attacker can put {env.DATABASE_URL} or {file./etc/passwd} in a request header and the server will evaluate it, leaking environment variables, file contents, and system info. header_regexp does NOT do this — it passes header …

Caddy forward_auth copy_headers Does Not Strip Client-Supplied Headers, Allowing Identity Injection and Privilege Escalation

Caddy's forward_auth directive with copy_headers generates conditional header-set operations that only fire when the upstream auth service includes the named header in its response. No delete or remove operation is generated for the original client-supplied request header with the same name. When an auth service returns 200 OK without one of the configured copy_headers headers, the client-supplied header passes through unchanged to the backend. Any requester holding a valid authentication …

Caddy: Unicode case-folding length expansion causes incorrect split_path index in FastCGI transport

Caddy's FastCGI path splitting logic computes the split index on a lowercased copy of the request path and then uses that byte index to slice the original path. This is unsafe for Unicode because strings.ToLower() can change UTF-8 byte length for some characters. As a result, Caddy can derive an incorrect SCRIPT_NAME/SCRIPT_FILENAME and PATH_INFO, potentially causing a request that contains .php to execute a different on-disk file than intended (path …

Caddy: Unicode case-folding length expansion causes incorrect split_path index in FastCGI transport

Caddy's FastCGI path splitting logic computes the split index on a lowercased copy of the request path and then uses that byte index to slice the original path. This is unsafe for Unicode because strings.ToLower() can change UTF-8 byte length for some characters. As a result, Caddy can derive an incorrect SCRIPT_NAME/SCRIPT_FILENAME and PATH_INFO, potentially causing a request that contains .php to execute a different on-disk file than intended (path …

Caddy: mTLS client authentication silently fails open when CA certificate file is missing or malformed

Two swallowed errors in ClientAuthentication.provision() cause mTLS client certificate authentication to silently fail open when a CA certificate file is missing, unreadable, or malformed. The server starts without error but accepts any client certificate signed by any system-trusted CA, completely bypassing the intended private CA trust boundary.

Caddy: mTLS client authentication silently fails open when CA certificate file is missing or malformed

Two swallowed errors in ClientAuthentication.provision() cause mTLS client certificate authentication to silently fail open when a CA certificate file is missing, unreadable, or malformed. The server starts without error but accepts any client certificate signed by any system-trusted CA, completely bypassing the intended private CA trust boundary.

Caddy: MatchPath %xx (escaped-path) branch skips case normalization, enabling path-based route/auth bypass

Caddy's HTTP path request matcher is intended to be case-insensitive, but when the match pattern contains percent-escape sequences (%xx) it compares against the request's escaped path without lowercasing. An attacker can bypass path-based routing and any access controls attached to that route by changing the casing of the request path.

Caddy: MatchPath %xx (escaped-path) branch skips case normalization, enabling path-based route/auth bypass

Caddy's HTTP path request matcher is intended to be case-insensitive, but when the match pattern contains percent-escape sequences (%xx) it compares against the request's escaped path without lowercasing. An attacker can bypass path-based routing and any access controls attached to that route by changing the casing of the request path.

Caddy: MatchHost becomes case-sensitive for large host lists (>100), enabling host-based route/auth bypass

Caddy's HTTP host request matcher is documented as case-insensitive, but when configured with a large host list (>100 entries) it becomes case-sensitive due to an optimized matching path. An attacker can bypass host-based routing and any access controls attached to that route by changing the casing of the Host header.

Caddy is vulnerable to cross-origin config application via local admin API /load

The local caddy admin API (default listen 127.0.0.1:2019) exposes a state-changing POST /load endpoint that replaces the entire running configuration. When origin enforcement is not enabled (enforce_origin not configured), the admin endpoint accepts cross-origin requests (e.g., from attacker-controlled web content in a victim browser) and applies an attacker-supplied JSON config. this can change the admin listener settings and alter HTTP server behavior without user intent.

2023

Authentication Bypass by Spoofing

The caddy-geo-ip (aka GeoIP) middleware through 0.6.0 for Caddy 2, when trust_header X-Forwarded-For is used, allows attackers to spoof their source IP address via an X-Forwarded-For header, which may bypass a protection mechanism (trusted_proxy directive in reverse_proxy or IP address range restrictions).

2022
2018

Information Exposure

Caddy sends incorrect certificates for certain invalid requests, making it easier for attackers to enumerate hostnames. Specifically, when unable to match a Host header with a vhost in its configuration, it serves the X.509 certificate for a randomly selected vhost in its configuration. Repeated requests (with a nonexistent hostname in the Host header) permit full enumeration of all certificates on the server. This generally permits an attacker to easily and …