Feb 21, 2026·6 min read·260 visits
Next.js implemented a loop-prevention mechanism that trusted a specific header to track middleware recursion. Attackers can manually inject this header (`x-middleware-subrequest`) to convince the server that the limit has been reached, causing Next.js to skip security middleware and serve protected content.
A critical authorization bypass vulnerability in Next.js allows attackers to skip middleware execution entirely. By spoofing an internal recursion-tracking header, attackers can trick the server into believing a request has already been processed multiple times, causing it to bypass authentication checks and access protected routes directly. This affects all versions from 11.1.4 up to 15.2.2.
Middleware in Next.js is the unsung hero of modern React applications. It sits at the edge, acting as the bouncer for your routes. It checks if you have a session token, validates your permissions, rewrites paths based on your locale, and injects security headers like CSP. It is the gatekeeper. If the middleware doesn't run, the gate is effectively left wide open.
But middleware has a problem: recursion. Since middleware can rewrite requests to other paths which might trigger more middleware, there is a risk of creating an infinite loop that crashes the server (or at least the Vercel function, costing you a fortune in compute time). To solve this, Next.js needs a way to track how deep the rabbit hole goes.
The engineers implemented a solution that is logical on paper but catastrophic in practice: they used an HTTP header to count the loops. Specifically, x-middleware-subrequest. The server reads this header, counts the occurrences of the middleware name, and if it hits a hardcoded limit (usually 5), it assumes a loop is happening and... stops executing the middleware to save the process.
Here is the kicker: they trusted the client to provide the truth. They assumed that this header would only ever be present if the server put it there during an internal rewrite. They forgot the first rule of web security: Input is evil. If an attacker simply sends this header in their initial request, the server throws its hands up, says 'Whoa, too many loops!', skips the security checks, and hands over the data.
The vulnerability stems from a classic logic flaw: relying on client-controlled data for server-side state management without validation. In versions of Next.js ranging from v12.2 to v15.2.2, the logic for handling the x-middleware-subrequest header shifted from a simple path check to a recursion depth check. The code looks for a colon-separated list of middleware names in the header.
Technically, the server parses x-middleware-subrequest. It splits the string by colons and counts how many times the current middleware's name appears. If that count exceeds MAX_RECURSION_DEPTH (which is set to 5), the runMiddleware function essentially returns early, passing the request downstream to the page handler or API route without running the user-defined logic in middleware.ts.
This is the architectural equivalent of a bank vault that locks automatically at 5:00 PM, but checks the time by looking at the customer's wristwatch. If you walk in at noon and tell the teller 'It's 5:01 PM', they unlock the vault and go home, leaving you inside. Because the header is processed before any user code runs, there is nothing a developer can write in their own middleware.ts to stop this. The framework fails before your code even loads.
Let's look at the logic that caused the issue. While the exact file paths vary by version, the core logic in the adapter.ts or next-server components effectively did this:
// Pseudocode of the vulnerable logic
const MAX_RECURSION_DEPTH = 5;
function getMiddlewareRecursion(req) {
const header = req.headers.get('x-middleware-subrequest') || '';
const depth = header.split(':').filter(n => n === middlewareName).length;
return depth;
}
// Later in the request lifecycle...
if (getMiddlewareRecursion(req) >= MAX_RECURSION_DEPTH) {
// Assume infinite loop, skip middleware execution
return { finished: true };
}The fix, introduced in commit 52a078da3884efe6501613c7834a3d02a91676d2 (and backported in 5fd3ae8f8542677c6294f32d18022731eab6fe48), introduces a shared secret. The server now generates a random token at startup. Internal sub-requests must sign the header with this token.
// The Fix: Validation of the source
if (requestHeaders.has('x-middleware-subrequest')) {
const subrequestId = requestHeaders.get('x-middleware-subrequest-id');
// Check if the secret matches what the server generated at startup
if (subrequestId !== internalSecret) {
// If it doesn't match, the client is spoofing it. Strip the header.
requestHeaders.delete('x-middleware-subrequest');
}
}This ensures that only the Next.js server itself can increment the recursion counter. If an external attacker tries to inject the header, they won't know the random 8-byte hex string (stored in a Symbol in globalThis), so the server strips their fake header and runs the middleware as normal.
Exploiting this requires zero coding skills and standard command-line tools. You don't need buffer overflows; you just need to know how to count to five.
The Scenario: You have a Next.js application protecting an admin panel at /admin. The middleware checks for a session cookie and redirects to /login if it's missing.
The Attack:
/admin. Receive 307 Temporary Redirect to /login.middleware or src/middleware.curl -v http://target.com/admin \
-H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware"The Result: The server sees the header, calculates depth = 5, triggers the loop prevention logic, skips the auth check, and returns 200 OK with the contents of the admin dashboard.
> [!WARNING]
> In some setups, the middleware name might include the path, like pages/_middleware. Exploitation tools like Nuclei simply spray variants: src/middleware:middleware:pages/_middleware... to cover all bases.
The immediate impact is obvious: broken access control. If you use Next.js middleware to gate content, that gate is gone. Admin panels, user dashboards, and premium content are accessible to anyone.
However, the secondary impact is potentially worse: Web Cache Poisoning.
Many Next.js sites sit behind CDNs (Vercel Edge Network, Cloudflare, Fastly). If an attacker sends the exploit request and the CDN is configured to cache the response (even briefly), the CDN might cache the bypassed version of the page (the 200 OK admin panel) instead of the redirect.
Now, legitimate administrators or users navigating to that URL will be served the cached, exploited content. Or worse, if the page renders user-specific data (PII) relying on the middleware to filter it, an attacker could force the server to generate a page with their own malicious state, cache it, and serve it to victims. This turns a targeted bypass into a broad Denial-of-Service or Information Disclosure event.
The vulnerability is patched in Next.js versions 15.2.3, 14.2.25, 13.5.9, and 12.3.5. The patch logic relies on stripping the malicious header if a corresponding secret ID is absent.
Immediate Remediation Strategy:
next dependency immediately. npm install next@latest.Mitigation (If you cannot upgrade): If you are stuck on a legacy version or cannot deploy immediately, you must implement a block at your network edge.
Nginx Configuration:
# Drop the header before it hits the node process
proxy_set_header x-middleware-subrequest "";Cloudflare WAF Rule:
Create a firewall rule blocking any request where the header x-middleware-subrequest exists. Since this header is internal-only, legitimate clients (browsers) never send it. It is safe to drop all external traffic containing it.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Next.js Vercel | 11.1.4 - 12.3.4 | 12.3.5 |
Next.js Vercel | 13.0.0 - 13.5.8 | 13.5.9 |
Next.js Vercel | 14.0.0 - 14.2.24 | 14.2.25 |
Next.js Vercel | 15.0.0 - 15.2.2 | 15.2.3 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-285 (Improper Authorization) |
| Attack Vector | Network (HTTP Headers) |
| CVSS | 9.1 (Critical) |
| EPSS Score | 0.92896 (92.90%) |
| Impact | Full Auth Bypass |
| Exploit Status | Active / POC Available |
The software does not perform or incorrectly performs an authorization check when an actor attempts to access a resource or perform an action.
This report details CVE-2026-91127 (GHSA-3753-m2x2-q623), a high-severity DOM Cross-Site Scripting (DOM XSS) vulnerability in the file-viewer workspace developed by flyfish-dev. The legacy Word document (.doc) parser fails to restrict hyperlink URI schemes when rendering extracted document targets into generated HTML. As a result, a remote attacker can construct a malicious legacy DOC file containing scripts inside hyperlink properties. When a user previews the file and clicks the hyperlink, arbitrary JavaScript executes in the context of the hosting origin, enabling session hijacking, credential theft, or unauthorized API interaction.
An authorization bypass and tenant isolation vulnerability in Perses prior to version 0.54.0-beta.3 allows authenticated viewers to access unauthorized project resources by manipulating query parameters or querying unmapped ephemeral endpoints.
CVE-2026-63199 is a critical missing authorization vulnerability (CWE-862) in Perses versions 0.43.0 to 0.54.0-rc.0. It allows low-privileged attackers to retrieve and exfiltrate highly sensitive credentials (secrets) from different scopes by configuring a malicious datasource pointing to an attacker-controlled endpoint.
An arbitrary file read and path traversal vulnerability exists in Perses prior to version 0.54.0-rc.0. When configured with a file-system database backend, the application lacks input validation on the request-controlled project query parameter. An authenticated attacker with low privileges can supply directory traversal sequences to read arbitrary JSON or YAML files on the host file system.
CVE-2026-59163 is a critical authentication bypass vulnerability in the Mnemosyne sync server. In versions prior to v3.10.1, the server's authentication logic decoded incoming JSON Web Tokens (JWT) but completely skipped cryptographic signature verification. An unauthenticated remote attacker can exploit this vulnerability to bypass authentication, impersonate arbitrary users, read synchronized AI agent states, or write malicious database updates.
An authorization bypass vulnerability exists in the Moquette MQTT broker prior to version 0.18.1. When an MQTT client registers a Last Will and Testament (LWT) topic during its connection setup, the broker fails to perform write-access checks on that topic. Upon an abrupt client disconnection, the broker publishes the registered Will message to subscribers of the unauthorized topic, bypassing configured Access Control Lists (ACLs).