Breach report
Next.js middleware: one spoofed header skipped authentication entirely
What happened
Next.js middleware runs before a request reaches a route and is a common place to enforce authentication and authorization. On March 21, 2025, CVE-2025-29927 was disclosed: an authorization bypass in self-hosted Next.js deployments with a CVSS score of 9.1. Next.js uses an internal header, x-middleware-subrequest, to mark requests it generates itself so they don't loop back through middleware. The flaw is that this header was trusted when it arrived from outside.
An attacker simply adds the header to an inbound request, and "the Next.js internal logic then reads the header and completely bypasses the middleware, knowing that this request is an internal subrequest." In 15.x, a repeated value like middleware:middleware:middleware:middleware:middleware defeats a recursion-depth guard. Any auth check living in middleware is skipped. Fixed in 12.3.5, 13.5.9, 14.2.25, and 15.2.3; Vercel-hosted, Netlify, and static-export deployments were unaffected.
Root cause
The framework trusted an internal control signal supplied by the client — improper authorization, CWE-285, and OWASP's #1 category, Broken Access Control. A header that should only ever be set internally was accepted from external requests, so the client could assert "I am an internal subrequest, skip the gate."
How it would have been caught
A negative authorization test — request a protected route while injecting x-middleware-subrequest and assert it is still blocked — fails loudly against the vulnerable version. More broadly, tests should confirm protected routes deny access regardless of client-supplied headers. A reproduction sends the header at a guarded endpoint and confirms it returns protected content.
How to prevent it
- Never trust client-supplied "internal" headers; strip inbound
x-middleware-subrequestat the edge or upgrade to a patched version. - Enforce authorization at the data/route layer too, so a bypassed middleware isn't the only gate (defense in depth).
- Test access control negatively against header, path, and method manipulation.
The Breachwire test (red → green)
Request a protected route with x-middleware-subrequest injected and confirm it returns protected content — the RED control. Upgrade to the patched version (or strip the header at the proxy), then confirm the same request is denied while legitimate authenticated sessions still reach the route.