NextjsVulnerability

Critical Next.js Auth Bypass – CVE-2025-29927 Explained

Baptiste Loison
BL

Baptiste Loison

Security Engineer

Hello everyone! Just two days ago, two vulnerability researchers (zhero and inzo) disclosed what is arguably the most severe security flaw ever discovered in Next.js. If you're building your application on Next.js, this vulnerability could allow attackers to bypass authentication and access protected customer data—even admin dashboards—without triggering any security controls.

The Next.js team has quickly fixed the issue on the latest versions; however, all versions of Next.js from 11.1.4 through 13.5.6, 14.x before 14.2.25, and 15.x before 15.2.3 are affected by this vulnerability.

This week, we'll look at how to prevent this issue, understand how the vulnerability arises, explore how it can be exploited, and examine how the Vercel team developed the patch.

TL;DR

  • ✅ If using Vercel, Netlify, or Cloudflare – you’re safe; they patched it automatically.
  • ⚠️ If self-hosting with next start or output: 'standalone' – upgrade to the latest Next.js immediately.

A glimpse into the vulnerability

The vulnerability lies in the x-middleware-subrequest header that is used by Next.js to prevent recursive requests from triggering infinite loops. This header counts the number of times a middleware is called. The header is supposed to be used only internally by the application, but nothing prevents a malicious user from adding the header manually.

The code and exploit vary slightly depending on the version of Next.js, but for the latest version at the time (Next.js 15.1.7). The vulnerability worked as follows:

the router reads the x-middleware-subrequest header and splits its value by the ':' character. It then counts how many times the middleware's name appears in this header. If this count exceeds the maximum recursion depth (5), the middleware execution is bypassed entirely, and the request proceeds to the next step in the processing pipeline.

Below is the section of the Next.js code responsible for the vulnerability :

const subreq = params.request.headers[`x-middleware-subrequest`];
const subrequests = typeof subreq === "string" ? subreq.split(":") : [];

const MAX_RECURSION_DEPTH = 5;
const depth = subrequests.reduce(
  (acc, curr) => (curr === params.name ? acc + 1 : acc),
  0
);

if (depth >= MAX_RECURSION_DEPTH) {
  return {
    waitUntil: Promise.resolve(),
    response: new runtime.context.Response(null, {
      headers: {
        "x-middleware-next": "1",
      },
    }),
  };
}

from Next.js 15.1.7 - packages/next/src/server/web/sandbox/sandbox.ts#L94

This effectively allows an attacker to craft a request with a specially formatted header, a x-middleware-subrequest header with the name of the middleware 5 times or more, that tricks Next.js into skipping crucial security checks.

If you are curious you can find a proof of concept of an exploit using this vulnerability here

How the Vercel team fixed it

To address this issue, the Vercel team decided to generate a cryptographic random ID at the beginning of the request, store it in a global variable and add it as a new header to the request at the same time.

const randomBytes = new Uint8Array(8)
crypto.getRandomValues(randomBytes)
const middlewareSubrequestId = Buffer.from(randomBytes).toString('hex');
(globalThis as any)[Symbol.for('@next/middleware-subrequest-id')] = middlewareSubrequestId

// Later ...

init.headers.set(
    'x-middleware-subrequest-id',
    (globalThis as any)[Symbol.for('@next/middleware-subrequest-id')]
)

When arriving at a middleware, it checks if the subrequest ID corresponds to the one generated earlier. If it matches, then the request originates from the NextJS router; otherwise, the request originates from outside, and so we remove the x-middleware-subrequest header.

if (
    header === 'x-middleware-subrequest' &&
    headers['x-middleware-subrequest-id'] !==
    (globalThis as any)[Symbol.for('@next/middleware-subrequest-id')]
) {
    delete headers['x-middleware-subrequest']
}

The full pull request that fixes the issue for the Next.js 15.X versions can be found right here https://github.com/vercel/next.js/pull/77201

Stay updated on cybersecurity

Get weekly security tips for SaaS founders and indie makers.