Every CVE Search/ Get API key

CVE-2026-77281

Caddy: rewrite placeholder re-expansion

6.5 MediumFix available

Caddy is an extensible server platform that uses TLS by default. In version 2.11.3 and earlier, three configuration-dependent weaknesses affect the handler and placeholder layer. In modules/caddyhttp/rewrite/rewrite.go, Rewrite.Rewrite() can pass attacker-controlled replacement bytes through buildQueryString for a second placeholder expansion when a rewrite URI ends with a literal question mark, allowing injected environment or request-variable placeholders to disclose data and, when the file provider is registered, allowing injected file placeholders to disclose readable files. The issue is fixed in version 2.11.4.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
None
User interaction
None

What an attacker can affect

Confidentiality
Low
Integrity
None
Availability
Low

Exploit likelihood

0.49%Up 0.1 points since Sep 18, 2026
90%50%10%1%

What each source reports

CVE List Score used 6.5

Caddy: rewrite placeholder re-expansion

Caddy is an extensible server platform that uses TLS by default. In version 2.11.3 and earlier, three configuration-dependent weaknesses affect the handler and placeholder layer. In modules/caddyhttp/rewrite/rewrite.go, Rewrite.Rewrite() can pass attacker-controlled replacement bytes through buildQueryString for a second placeholder expansion when a rewrite URI ends with a literal question mark, allowing injected environment or request-variable placeholders to disclose data and, when the file provider is registered, allowing injected file placeholders to disclose readable files. The issue is fixed in version 2.11.4.

Record
CVE-2026-77281
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:L
Weakness
CWE-94, CWE-178, CWE-770
Updated
2026-11-02
EUVDEUVD-2026-82474 6.5

Caddy is an extensible server platform that uses TLS by default. In version 2.11.3 and earlier, three configuration-dependent weaknesses affect the handler and placeholder layer. In modules/caddyhttp/rewrite/rewrite.go, Rewrite.Rewrite() can pass attacker-controlled replacement bytes through buildQueryString for a second placeholder expansion when a rewrite URI ends with a literal question mark, allowing injected environment or request-variable placeholders to disclose data and, when the file provider is registered, allowing injected file placeholders to disclose readable files. The issue is fixed in version 2.11.4.

Record
EUVD-2026-82474
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:L
Updated
2026-11-02
GitHub AdvisoriesGHSA-j8px-rmrx-76h9 6.5

Caddy: rewrite placeholder re-expansion, unbounded body buffer DoS, and fileHidden case-sensitivity bypass

Caddy v2.11.3 — Three vulnerabilities in handler/placeholder layer

Tested against: caddy:2.11.3 (official Docker image, SHA verified at runtime) Reproduction environment: Docker Desktop 4.73.1 / Engine 29.4.3 on Windows 10 host, isolated containers, no network egress required for any of the exploits

This advisory bundles three independent issues discovered together during a source review of the placeholder/replacer layer. Each issue has been reproduced end-to-end against the unmodified caddy:2.11.3 image with the minimal Caddyfile that the documentation suggests for the affected feature.


Issue 1: Rewrite handler — placeholder double-expansion enables env var / file disclosure

File: modules/caddyhttp/rewrite/rewrite.go:215-249 and buildQueryString at 327 Class: CWE-94 (Code Injection), same bug class as CVE-2026-30852 (vars_regexp) Severity: Low (requires operator config with trailing ? in rewrite URI)

Root cause

When the operator's rewrite URI template:

  1. Contains a placeholder that resolves to request data (e.g. {http.request.header.X-Foo}), AND
  2. Ends with a literal ? (with empty query side)

…then the bytes produced by the first Replacer pass (which include attacker-controlled header values) are fed through buildQueryString, which runs a second Replacer pass and resolves any placeholders the attacker injected.

// rewrite.go (abridged)
newPath = repl.ReplaceAll(path, "")            // pass 1 — header expanded
if before, after, found := strings.Cut(newPath, "?"); found {
    var injectedQuery string
    newPath, injectedQuery = before, after
    if query == "" {                            // trailing-? branch
        query = injectedQuery                   // attacker bytes flow into 'query'
    }
}
if query != "" {
    newQuery = buildQueryString(query, repl)    // pass 2 — RE-EXPANDS attacker input
}

This is the same gadget that was patched in vars_regexp (CVE-2026-30852). The fix did not extend to rewrite, and there is no equivalent regression test for it in rewrite_test.go (compare vars_test.go:63,69,75).

Reproduction (real Caddy 2.11.3)

Caddyfile:

{
    admin off
    auto_https off
}

:8080 {
    rewrite * /serve/{http.request.header.X-Fwd}?
    respond "PATH={path} QUERY={query}"
}

docker-compose.yml:

services:
  caddy:
    image: caddy:2.11.3
    environment:
      DATABASE_URL: "postgres://leaked:supersecret@dbserver/production"
    ports: ["8080:8080"]
    volumes: ["./Caddyfile:/etc/caddy/Caddyfile:ro"]

Exploit:

$ docker compose up -d
$ curl "http://localhost:8080/anything" -H "X-Fwd: foo?{env.DATABASE_URL}=leak"
PATH=/serve/foo QUERY=postgres%3A%2F%2Fleaked%3Asupersecret%40dbserver%2Fproduction=leak

URL-decoded query: postgres://leaked:supersecret@dbserver/production=leak. The DATABASE_URL env var has been exfiltrated into the request URL, where it will appear in access logs, get forwarded to upstreams via reverse_proxy, and be readable via {http.request.uri.query} in any downstream handler.

Available read primitives

The same gadget exposes any placeholder the attacker can name in their injected substring:

  • {env.X} — any env var on the Caddy process
  • {file./path} — any file readable by the Caddy process (if file provider is registered)
  • {vars.X} — Caddy-internal request variables

Suggested fix (mirrors the CVE-2026-30852 patch)

After splitting at ?, sanitize placeholder syntax in the injected query before passing it to buildQueryString:

if before, after, found := strings.Cut(newPath, "?"); found {
    var injectedQuery string
    newPath, injectedQuery = before, after
    if query == "" {
        injectedQuery = strings.ReplaceAll(injectedQuery, "{", "%7B")
        injectedQuery = strings.ReplaceAll(injectedQuery, "}", "%7D")
        query = injectedQuery
    }
}

Also recommend adding equivalent regression tests in…

Record
GHSA-j8px-rmrx-76h9
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:L
Weakness
CWE-94, CWE-178, CWE-770
Updated
2026-11-02
NVD 6.5

Caddy is an extensible server platform that uses TLS by default. In version 2.11.3 and earlier, three configuration-dependent weaknesses affect the handler and placeholder layer. In modules/caddyhttp/rewrite/rewrite.go, Rewrite.Rewrite() can pass attacker-controlled replacement bytes through buildQueryString for a second placeholder expansion when a rewrite URI ends with a literal question mark, allowing injected environment or request-variable placeholders to disclose data and, when the file provider is registered, allowing injected file placeholders to disclose readable files. The issue is fixed in version 2.11.4.

Record
CVE-2026-77281
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:L
Weakness
CWE-94, CWE-178, CWE-770
Updated
2026-11-02
OSV 6.5

Caddy: rewrite placeholder re-expansion

Caddy is an extensible server platform that uses TLS by default. In version 2.11.3 and earlier, three configuration-dependent weaknesses affect the handler and placeholder layer. In modules/caddyhttp/rewrite/rewrite.go, Rewrite.Rewrite() can pass attacker-controlled replacement bytes through buildQueryString for a second placeholder expansion when a rewrite URI ends with a literal question mark, allowing injected environment or request-variable placeholders to disclose data and, when the file provider is registered, allowing injected file placeholders to disclose readable files. The issue is fixed in version 2.11.4.

Record
CVE-2026-77281
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:L
Weakness
CWE-178, CWE-770, CWE-94
Updated
2026-11-02
OSVCVE-2026-92284 6.9

Caddy: Unbounded body buffer via {http.request.body} placeholder — memory exhaustion DoS

Caddy is an extensible server platform that uses TLS by default. In version 2.11.3 and earlier, in modules/caddyhttp/replacer.go, resolving http.request.body reads the complete request body with an unbounded io.Copy before request-body middleware limits apply, allowing memory exhaustion and process termination.

Record
CVE-2026-92284
CVSS 4.0
6.9 MediumCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N
Weakness
CWE-770
Updated
2026-11-02
Show 5 moreShow less
OSVCVE-2026-92700 6.3

Caddy: fileHidden() case-sensitive pattern bypass — exposes "hidden" files via case variation

Caddy is an extensible server platform that uses TLS by default. In version 2.11.3 and earlier, in modules/caddyhttp/fileserver/staticfiles.go, fileHidden() uses case-sensitive filepath.Match checks, so case variants can bypass hide rules on case-insensitive filesystems or when mixed-case paths coexist and expose files intended to be hidden.

Record
CVE-2026-92700
CVSS 4.0
6.3 MediumCVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N
Weakness
CWE-178
Updated
2026-11-02
OSVDEBIAN-CVE-2026-77281 6.5

Caddy is an extensible server platform that uses TLS by default. In version 2.11.3 and earlier, three configuration-dependent weaknesses affect the handler and placeholder layer. In modules/caddyhttp/rewrite/rewrite.go, Rewrite.Rewrite() can pass attacker-controlled replacement bytes through buildQueryString for a second placeholder expansion when a rewrite URI ends with a literal question mark, allowing injected environment or request-variable placeholders to disclose data and, when the file provider is registered, allowing injected file placeholders to disclose readable files. The issue is fixed in version 2.11.4.

Record
DEBIAN-CVE-2026-77281
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:L
Updated
2026-11-02
OSVGHSA-j8px-rmrx-76h9 6.5

Caddy: rewrite placeholder re-expansion, unbounded body buffer DoS, and fileHidden case-sensitivity bypass

Caddy v2.11.3 — Three vulnerabilities in handler/placeholder layer

Tested against: caddy:2.11.3 (official Docker image, SHA verified at runtime) Reproduction environment: Docker Desktop 4.73.1 / Engine 29.4.3 on Windows 10 host, isolated containers, no network egress required for any of the exploits

This advisory bundles three independent issues discovered together during a source review of the placeholder/replacer layer. Each issue has been reproduced end-to-end against the unmodified caddy:2.11.3 image with the minimal Caddyfile that the documentation suggests for the affected feature.


Issue 1: Rewrite handler — placeholder double-expansion enables env var / file disclosure

File: modules/caddyhttp/rewrite/rewrite.go:215-249 and buildQueryString at 327 Class: CWE-94 (Code Injection), same bug class as CVE-2026-30852 (vars_regexp) Severity: Low (requires operator config with trailing ? in rewrite URI)

Root cause

When the operator's rewrite URI template:

  1. Contains a placeholder that resolves to request data (e.g. {http.request.header.X-Foo}), AND
  2. Ends with a literal ? (with empty query side)

…then the bytes produced by the first Replacer pass (which include attacker-controlled header values) are fed through buildQueryString, which runs a second Replacer pass and resolves any placeholders the attacker injected.

// rewrite.go (abridged)
newPath = repl.ReplaceAll(path, "")            // pass 1 — header expanded
if before, after, found := strings.Cut(newPath, "?"); found {
    var injectedQuery string
    newPath, injectedQuery = before, after
    if query == "" {                            // trailing-? branch
        query = injectedQuery                   // attacker bytes flow into 'query'
    }
}
if query != "" {
    newQuery = buildQueryString(query, repl)    // pass 2 — RE-EXPANDS attacker input
}

This is the same gadget that was patched in vars_regexp (CVE-2026-30852). The fix did not extend to rewrite, and there is no equivalent regression test for it in rewrite_test.go (compare vars_test.go:63,69,75).

Reproduction (real Caddy 2.11.3)

Caddyfile:

{
    admin off
    auto_https off
}

:8080 {
    rewrite * /serve/{http.request.header.X-Fwd}?
    respond "PATH={path} QUERY={query}"
}

docker-compose.yml:

services:
  caddy:
    image: caddy:2.11.3
    environment:
      DATABASE_URL: "postgres://leaked:supersecret@dbserver/production"
    ports: ["8080:8080"]
    volumes: ["./Caddyfile:/etc/caddy/Caddyfile:ro"]

Exploit:

$ docker compose up -d
$ curl "http://localhost:8080/anything" -H "X-Fwd: foo?{env.DATABASE_URL}=leak"
PATH=/serve/foo QUERY=postgres%3A%2F%2Fleaked%3Asupersecret%40dbserver%2Fproduction=leak

URL-decoded query: postgres://leaked:supersecret@dbserver/production=leak. The DATABASE_URL env var has been exfiltrated into the request URL, where it will appear in access logs, get forwarded to upstreams via reverse_proxy, and be readable via {http.request.uri.query} in any downstream handler.

Available read primitives

The same gadget exposes any placeholder the attacker can name in their injected substring:

  • {env.X} — any env var on the Caddy process
  • {file./path} — any file readable by the Caddy process (if file provider is registered)
  • {vars.X} — Caddy-internal request variables

Suggested fix (mirrors the CVE-2026-30852 patch)

After splitting at ?, sanitize placeholder syntax in the injected query before passing it to buildQueryString:

if before, after, found := strings.Cut(newPath, "?"); found {
    var injectedQuery string
    newPath, injectedQuery = before, after
    if query == "" {
        injectedQuery = strings.ReplaceAll(injectedQuery, "{", "%7B")
        injectedQuery = strings.ReplaceAll(injectedQuery, "}", "%7D")
        query = injectedQuery
    }
}

Also recommend adding equivalent regression tests in…

Record
GHSA-j8px-rmrx-76h9
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:L
Weakness
CWE-178, CWE-770, CWE-94
Updated
2026-11-02
OSVUBUNTU-CVE-2026-77281 6.5

Caddy is an extensible server platform that uses TLS by default. In version 2.11.3 and earlier, three configuration-dependent weaknesses affect the handler and placeholder layer. In modules/caddyhttp/rewrite/rewrite.go, Rewrite.Rewrite() can pass attacker-controlled replacement bytes through buildQueryString for a second placeholder expansion when a rewrite URI ends with a literal question mark, allowing injected environment or request-variable placeholders to disclose data and, when the file provider is registered, allowing injected file placeholders to disclose readable files. The issue is fixed in version 2.11.4.

Record
UBUNTU-CVE-2026-77281
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:L
Updated
2026-11-02
OSVGO-2026-6512 No score

Vulnerabilities in handler/placeholder layer in github.com/caddyserver/caddy

Rewrite placeholder re-expansion, unbounded body buffer DoS, and fileHidden case-sensitivity bypass in github.com/caddyserver/caddy

Record
GO-2026-6512
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
github.com/caddyserver/caddy/v2
Go
through 2.11.3
2.11.4
caddy
Debian:12
before 2.6.2-5+deb12u1, plus listed versions
2.6.2-5
2.6.2-5+deb12u1
caddy
Debian:13
All versions, plus listed versions
2.11.2-12.11.2-1~bpo13+12.11.4-12.6.2-122.6.2-12+deb13u12.6.2-132.6.2-14
None known
caddy
Debian:14
before 2.11.4-1, plus listed versions
2.11.2-12.11.2-1~bpo13+12.6.2-122.6.2-132.6.2-14
2.11.4-1
github.com/caddyserver/caddy/v2
Go
From an advisory covering 3 CVEs
before 2.11.4
2.11.4
caddy
Ubuntu:Pro:24.04:LTS
All versions, plus listed versions
2.6.2-62.6.2-6ubuntu0.24.04.12.6.2-6ubuntu0.24.04.22.6.2-6ubuntu0.24.04.2+esm12.6.2-6ubuntu0.24.04.32.6.2-6ubuntu0.24.04.3+esm12.6.2-6ubuntu0.24.04.3+esm2
None known
caddy
Ubuntu:Pro:26.04:LTS
All versions, plus listed versions
2.6.2-122.6.2-142.6.2-14ubuntu0.26.04.1~esm1
None known

History

History
RecordedChangedSources
affected, facts
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-77281 is a medium vulnerability (CVSS 6.5) in Caddyserver Caddy, published September 17, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.49%, the 42nd percentile. Fixed in github.com/caddyserver/caddy/v2 2.11.4.

References

Show 9 moreShow less
This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open