Caddy: MatchHost becomes case-sensitive for large host lists (>100), enabling host-based route/auth bypass
Date: 2026-02
Affected: go/github.com/caddyserver/caddy/v2
Attack vector: auth-bypass
Category: vulnerability-disclosure
Severity: High
CVE: CVE-2026-27587
CVE-2026-27588
CWE: CWE-178

### Summary 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. ### Details In Caddy `v2.10.2`, the `MatchHost` matcher states it matches the Host value case-insensitively: - `modules/caddyhttp/matchers.go`: `type MatchHost matches requests by the Host value (case-insensitive).` However, in `MatchHost.MatchWithError`, when the host list is considered "large" (`len(m) > 100`): - `MatchHost.large()` returns true for `len(m) > 100` (`modules/caddyhttp/matchers.go`, around the `large()` helper). - The matcher takes a "fast path" using binary search over the sorted host list, and checks for an exact match using a case-sensitive string comparison (`m[pos] == reqHost`). - After the fast path fails, the fallback loop short-circuits for large lists by breaking as soon as it reaches the first non-fuzzy entry. For configs comprised of exact hostnames only (no wildcards/placeholders), this prevents the `strings.EqualFold(reqHost, host)` check from ever running. Net effect: with a host list length of 101 or more, changing only the casing of the incoming `Host` header can cause the `host` matcher to not match when it should. #### Suggested fix - Normalize exact hostnames to lower-case during `MatchHost.Provision` (at


Mitigations:

Architectural layers (MAESTRO):