• Agents
  • Docs
  • Pricing
  • Blog
Log in
Get started

Security for apps built with AI. Paste a URL, get a report, fix what matters.

Product

  • How it works
  • What we find
  • Pricing
  • Agents
  • MCP Server
  • CLI
  • GitHub Action

Resources

  • Blog
  • Docs
  • FAQ
  • Glossary

Security

  • Supabase Security
  • Next.js Security
  • Lovable Security
  • Cursor Security
  • Bolt Security

Legal

  • Privacy Policy
  • Terms of Service
  • Cookie Policy
  • Imprint
© 2026 Flowpatrol. All rights reserved.
Back to Blog
Preview — this article is scheduled for publication on Apr 1, 2026. It is not listed on the blog or indexed by search engines.
Case Study

CVE-2025-29927: The Next.js Middleware Bypass That Broke Auth With One Header

A single HTTP header could skip every middleware check in Next.js — authentication, authorization, CSP, rate limiting, all of it. Here's exactly how CVE-2025-29927 works, who's affected, and what to do about it.

Flowpatrol TeamApr 1, 202611 min read
CVE-2025-29927: The Next.js Middleware Bypass That Broke Auth With One Header

One header to skip them all

In March 2025, security researcher Rachid Allam disclosed one of the most consequential web framework vulnerabilities in recent memory. By adding a single HTTP header to any request, an attacker could completely bypass all Next.js middleware — authentication checks, authorization rules, CSP headers, rate limiting, geo-restrictions. Everything.

No credentials needed. No complex exploit chain. One curl command.

The vulnerability, CVE-2025-29927, scored a 9.1 on the CVSS scale (Critical). It affected every Next.js version from 11.x through 15.x, spanning years of production deployments across millions of applications. TikTok, Twitch, Hulu, Nike, and thousands of smaller apps all run on Next.js. If any of them relied on middleware for access control, they were exposed.

This is the full technical breakdown: what the vulnerability is, how it works under the hood, who was affected, and what you should do right now if you're building with Next.js.


What Next.js middleware does (and why this matters)

Next.js introduced middleware in version 12 as a way to run code before a request completes. You write a middleware.ts file, and it executes on every matching route before the page or API handler runs.

Most teams use middleware for security-critical functions:

// middleware.ts — a typical auth guard
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

export function middleware(request: NextRequest) {
  const token = request.cookies.get("session-token");

  if (!token) {
    return NextResponse.redirect(new URL("/login", request.url));
  }

  // Verify the token, check roles, etc.
  const user = verifyToken(token.value);
  if (!user) {
    return NextResponse.redirect(new URL("/login", request.url));
  }

  // User is authenticated — allow the request
  return NextResponse.next();
}

export const config = {
  matcher: ["/dashboard/:path*", "/admin/:path*", "/api/protected/:path*"],
};

This is the standard pattern. If you've ever built a Next.js app with protected routes, you've probably written something like this. It feels solid — every request to /dashboard, /admin, or /api/protected hits the middleware first, and unauthenticated users get redirected to login.

CVE-2025-29927 made all of it irrelevant.


How the bypass works

Under the hood, Next.js uses an internal HTTP header called x-middleware-subrequest to manage recursive middleware calls. When middleware makes a subrequest (like fetching data or calling another route), Next.js attaches this header to prevent infinite loops. Each subrequest increments a counter in the header value, and once it hits a threshold, the framework skips middleware execution entirely.

The problem: Next.js never validated where this header came from.

Diagram showing two request paths: normal requests pass through middleware, bypass requests skip it entirely

The header was designed for internal use between Next.js components. But nothing stopped an external client from setting it. And when an attacker sent a request with the right value in x-middleware-subrequest, the framework saw the recursion counter at its limit and skipped the middleware — exactly as designed, but for a request that should never have been trusted.

Here's the exploit:

# Bypass all middleware on a Next.js 15.x app
curl -H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware" \
     https://target.com/admin/dashboard

That's it. The /admin/dashboard route loads as if the middleware doesn't exist. No authentication check. No role validation. No redirect.

The exact header value varies slightly by version. For Next.js 14.x, where middleware can be in a src/ directory:

# Next.js 14.x with src/ directory
curl -H "x-middleware-subrequest: src/middleware:src/middleware:src/middleware" \
     https://target.com/admin

For older apps using the pages router:

# Pages router format
curl -H "x-middleware-subrequest: pages/_middleware" \
     https://target.com/api/sensitive

In every case, the result is the same: middleware never runs.


Why internal headers are a trap

This vulnerability falls into a well-known category: trusting data from the client. HTTP headers are entirely client-controlled. A server can't assume that any header value is authentic unless it has been cryptographically signed or validated at the network edge.

Next.js treated x-middleware-subrequest as an internal signal, but HTTP doesn't have a concept of "internal" headers. Every header in a request arrives through the same channel, whether it came from Next.js internals or from an attacker's terminal. Without validation, there's no way to distinguish one from the other.

The root cause maps to two CWE classifications:

  • CWE-285: Improper Authorization — the middleware bypass eliminates authorization entirely
  • CWE-290: Authentication Bypass by Spoofing — the spoofed header circumvents authentication checks

The fix in the patched versions was to either strip or validate the header before it reaches the middleware execution logic, ensuring external requests can't trigger the recursion-limit bypass.


What an attacker could do

The impact depends on what your middleware protects. For most Next.js apps, that means everything:

Authentication bypass. Access any protected page without logging in. Admin panels, user dashboards, internal tools — if the auth check lives in middleware, it's gone.

Authorization bypass. Even authenticated users have roles and permissions enforced in middleware. An attacker can escalate from a regular user to an admin, or access data they shouldn't see. (This is closely related to IDOR — another access control flaw that's rampant in AI-generated code.)

CSP bypass. Many apps set Content Security Policy headers in middleware. Bypassing middleware means the response comes back without CSP, opening the door to cross-site scripting attacks.

Rate limiting bypass. Middleware-based rate limiters stop working. Brute-force attacks against login endpoints, API abuse, scraping — all become possible without throttling.

Cache poisoning. If a CDN caches the response from a bypassed request, the poisoned response (without security headers, without auth checks) can be served to legitimate users.

Real attack chains

These aren't theoretical. Here's how an attacker would chain this:

Chain 1: Admin panel takeover
1. Identify the app runs Next.js (check for /_next/ paths)
2. Send GET /admin with the bypass header
3. Access the admin dashboard without credentials
4. Modify settings, export data, create backdoor accounts

Chain 2: Data exfiltration via API
1. Bypass auth middleware on /api/* routes
2. Hit user data endpoints directly
3. Enumerate and download sensitive records
4. No authentication logs generated (middleware never ran)

Chain 3: XSS via CSP bypass
1. Bypass middleware that sets CSP headers
2. Response arrives without Content-Security-Policy
3. Exploit a stored or reflected XSS vector
4. Steal session tokens, credentials, or user data

The third chain is particularly nasty. Even if you have other XSS mitigations, losing CSP removes a critical defense layer.


Affected versions

Every major version branch was vulnerable:

VersionVulnerablePatched
15.x< 15.2.315.2.3
14.x< 14.2.2514.2.25
13.x< 13.5.913.5.9
12.x< 12.3.512.3.5
11.x and earlierAll versionsNo patch (EOL)

If you're running Next.js 11 or earlier, there is no fix. You need to upgrade or mitigate at the network layer.


The timeline

The disclosure and response moved fast:

DateEvent
February 27, 2025Vulnerability reported to Next.js team via GitHub
March 17, 2025Next.js 14.2.25 released with fix
March 18, 2025Next.js 15.2.3 released with fix
March 18, 2025CVE-2025-29927 assigned
March 21, 2025Public disclosure and PoC exploits published
March 22, 2025Mass scanning observed in the wild

Note the gap: PoC code was public on March 21, and mass scanning started the next day. If you weren't patched by March 22, automated tools were already probing your app.


What you should do right now

1. Update Next.js

This is the primary fix. Update to the patched version for your branch:

# Latest (recommended)
npm install next@latest

# Or pin to a specific patched version
npm install next@15.2.3    # for 15.x
npm install next@14.2.25   # for 14.x
npm install next@13.5.9    # for 13.x
npm install next@12.3.5    # for 12.x

After updating, test your middleware. Make sure auth redirects still work, CSP headers are still set, and your protected routes are still protected.

2. Strip the header at your proxy (if you can't patch immediately)

If you're behind Nginx, Apache, or a reverse proxy, you can strip the dangerous header before it reaches Next.js:

# Nginx — strip the header from all incoming requests
proxy_set_header x-middleware-subrequest "";

Or block requests that include it entirely:

# Nginx — reject requests with the header
if ($http_x_middleware_subrequest) {
    return 403;
}

For Cloudflare, create a WAF rule:

Rule: (http.request.headers["x-middleware-subrequest"] exists)
Action: Block

3. Add detection logging

Even after patching, you should log attempts. This helps you understand if anyone was trying to exploit this before you patched:

// Add to the top of your middleware.ts
export function middleware(request: NextRequest) {
  const bypassHeader = request.headers.get("x-middleware-subrequest");
  if (bypassHeader) {
    console.warn("CVE-2025-29927 exploit attempt detected:", {
      ip: request.ip,
      path: request.nextUrl.pathname,
      header: bypassHeader,
      timestamp: new Date().toISOString(),
    });
    return new NextResponse("Forbidden", { status: 403 });
  }

  // ... rest of your middleware
}

4. Stop relying on middleware as your only security layer

This is the bigger lesson. Middleware is a convenience layer, not a security boundary. It's valuable for redirects, setting headers, and adding a first line of defense. But it should never be the only place you check authentication.

Add auth checks in your API routes:

// app/api/protected/route.ts
import { getServerSession } from "next-auth";

export async function GET(request: Request) {
  // Server-side auth check — independent of middleware
  const session = await getServerSession();
  if (!session) {
    return Response.json({ error: "Unauthorized" }, { status: 401 });
  }

  // ... handle the authenticated request
}

Add auth checks in your server components:

// app/admin/page.tsx
import { redirect } from "next/navigation";
import { getServerSession } from "next-auth";

export default async function AdminPage() {
  const session = await getServerSession();
  if (!session || session.user.role !== "admin") {
    redirect("/login");
  }

  return <AdminDashboard />;
}

These server-side checks run regardless of middleware. Even if middleware is bypassed — through this CVE or a future one — the route itself still enforces access control.


Defense in depth: the pattern that saves you

The Moltbook breach (Supabase RLS disabled), the Lovable platform vulnerability (no RLS on AI-generated apps), and now CVE-2025-29927 all share a root cause: a single security layer was the only security layer.

The pattern that protects against this entire class of problems is defense in depth:

LayerWhat it doesNext.js example
Edge/CDNStrip dangerous headers, block known attack patternsCloudflare WAF rule, Nginx header stripping
MiddlewareFirst-pass auth checks, redirects, security headersmiddleware.ts with session validation
Route handlerServer-side auth verification per endpointgetServerSession() in API routes
DatabaseRow-level access controlSupabase RLS, Prisma middleware

If any single layer fails, the others still hold. That's the point. Security isn't about having one perfect lock — it's about having locks at every door.


The vibe coding angle

If you're building with Cursor, Lovable, Bolt, v0, or any AI coding tool, this vulnerability has a specific implication: your AI assistant probably generated middleware-based auth as your primary security layer.

That's not a knock on the tools. Middleware auth is the documented, recommended pattern in Next.js. It's what the tutorials show. It's what AI models have been trained on. But CVE-2025-29927 proves that the recommended pattern isn't sufficient on its own.

When you ask an AI to "add authentication to my Next.js app," it'll generate middleware. It won't automatically add redundant server-side checks in every route handler. It won't configure WAF rules. It won't set up header stripping at the proxy layer. Those are defense-in-depth measures that you need to explicitly request — or explicitly verify.

This doesn't mean middleware auth is bad. It means middleware auth alone is incomplete. The fix is straightforward: after your AI generates the middleware, ask it to also add server-side auth checks in your route handlers and server components. Two layers instead of one.


How Flowpatrol catches this

CVE-2025-29927 is a textbook example of what automated security scanning should catch. Flowpatrol's scanner:

  1. Detects Next.js by fingerprinting the framework (the /_next/ path structure, response headers, and build artifacts are reliable signals)
  2. Tests for header-based bypasses by sending requests with the x-middleware-subrequest header and comparing responses with and without it
  3. Validates auth enforcement by checking whether protected routes actually require authentication at the server level, not just the middleware level
  4. Checks for defense in depth by verifying that multiple security layers are present, so a single bypass doesn't compromise everything

A scan takes minutes. The vulnerability it catches could have left your entire app wide open.


CVE-2025-29927 is documented in detail by Vercel's postmortem, ProjectDiscovery's technical analysis, Datadog Security Labs, and the NVD entry.

Back to all posts

More in Case Study

The Moltbook Breach: 1.5 Million API Tokens Exposed Because RLS Was Off
Mar 29, 2026

The Moltbook Breach: 1.5 Million API Tokens Exposed Because RLS Was Off

Read more
The Lovable RLS Vulnerability: How One AI Platform Shipped the Same Security Flaw Across 170+ Apps
Mar 28, 2026

The Lovable RLS Vulnerability: How One AI Platform Shipped the Same Security Flaw Across 170+ Apps

Read more