Traefik CVE-2025-66490 let encoded paths bypass middleware in affected releases, exposing split-view routing flaws in modern load balancers. Full breakdown.
What a Shadow Path Actually Is
A shadow path is a second, equally valid interpretation of the same request URL that the load balancer does not treat as identical to the first. One view is what routing and middleware see; another is what the upstream application eventually receives. When those views diverge, access-control rules can fire on one path while the real request still lands on a different resource.
Encoding is a common source of that split. Percent-encoded characters, overlong encodings, alternate slash forms, and case differences can survive early parsing in ways that later stages normalize or reverse. Traefik CVE-2025-66490 is a concrete case of this class of flaw: encoded paths could bypass middleware that should have run for the decoded, intended route. The product name and CVE matter less than the pattern—middleware attached to a path prefix or matcher never saw the request it was supposed to protect.
Why Split-View Routing Breaks Trust Boundaries
Modern load balancers sit in front of auth, rate limits, headers injection, and path rewriting. Operators assume a single canonical path string drives every decision. Shadow-path bugs violate that assumption. The balancer may match a rule against a cleaned or partially decoded path, then forward a still-encoded variant, or the reverse: match on the raw form and rewrite into something the origin treats as a different location.
That produces a classic split view. Security middleware believes it blocked or transformed the request. The application believes it received a normal client hit on a sensitive endpoint. Neither layer is lying in isolation; they simply disagree about which path was requested. Exhaustive unit tests that only exercise “pretty” URLs miss this entirely.
How to Reason About Path Matching Defensively
Treat path handling as a pipeline with an explicit normalization stage, not as a free-form string comparison. Decide once—early—what the canonical form is: decoding rules, slash collapse, trailing-slash policy, and case sensitivity. Apply middleware and routing only to that form, and send the same form (or a controlled rewrite of it) upstream. If a component must see the original bytes for logging or forensics, keep them separate from the string used for authorization.
- Normalize before match: decode and canonicalize once, then run every middleware and route rule against that result.
- Reject ambiguity: if decoding fails, produces control characters, or yields multiple plausible paths, fail closed rather than guessing.
- Align origin and edge: document how the reverse proxy and the app each interpret path segments so rewrites cannot reopen a shadow route.
- Test adversarial encodings: fuzz path matchers with mixed encodings of the same logical path and assert middleware still applies.
Practical Hardening When You Depend on Edge Middleware
Do not rely solely on the load balancer for authorization if the origin can interpret paths differently. Defense in depth means the application still enforces identity and scope on every request, even when a gateway “should have” stripped or blocked it. Prefer allowlists of known-safe path shapes over blocklists of encodings you have seen so far.
When reviewing gateway config after a shadow-path style issue, map each middleware chain to the exact matchers that trigger it, then prove—with request-level tests—that encoded and decoded variants of protected routes hit the same chain. Log both the raw request target and the post-normalization path so production incidents are diagnosable. The lesson from flaws like Traefik CVE-2025-66490 is not that edge proxies are unusable; it is that path equality is a security decision, and any layer that assumes two string forms are the same without proving it is inventing a second, invisible route.