CVE-2025-29927
Take a look at the Next.js middleware vulnerability CVE-2025-29927.
CVE-2025-29927 is a security issue that can bypass Next.js middleware authorization. Next.js uses the x-middleware-subrequest header to prevent infinite recursion during middleware execution.
If a user injects this header value through a proxy tool, middleware authorization can be invalidated, allowing access to critical server resources and potentially sensitive data.
Middleware and the vulnerability
Middleware is a common concept in frameworks like Rails and Laravel to reuse logic that would otherwise be duplicated on every page when handling incoming requests. Next.js middleware is a little different. It is a place to perform simple checks and routing before server component code executes, and it is not appropriate as the only authentication mechanism for an app.
After responding to this issue, the Next.js team removed authentication-related use cases for middleware from the official docs.
Middleware is fine for simple checks like the presence of a cookie value, but it is not appropriate to serve as the only auth component for the entire app.
export function middleware(request) {
const sessionCookie = request.cookies.get('session');
if (!sessionCookie) return Response.redirect('/login');
}
Validation that checks the database or verifies cookie validity should be performed in server components as well.
// lib/auth.ts
export async function validateSession(token?: string) {
if (!token) return null
const user = await db.user.findUnique({
where: { sessionToken: token },
select: { id: true, name: true, role: true }
})
return user
}
// app/dashboard/page.tsx
import { cookies } from 'next/headers'
import { redirect } from 'next/navigation'
import { validateSession } from '@/lib/auth'
export default async function DashboardPage() {
// Get cookies from headers
const cookieStore = cookies()
const sessionCookie = cookieStore.get('session')?.value
// Validate session on server
const user = await validateSession(sessionCookie)
// Redirect if validation fails
if (!user) {
redirect('/login')
}
// Render protected content
return (
<div>
<h1>Welcome, {user.name}</h1>
{/* Protected content */}
</div>
)
}
x-middleware-subrequest
The x-middleware-subrequest header value is not intended to be injected from outside the server and is used internally by Next.js to prevent infinite recursion between middleware calls. Internally, Next.js splits the header value on colons to create a list, then compares the count to MAX_RECURSION_DEPTH. If the count meets or exceeds the threshold, middleware is bypassed.
export const run = withTaggedErrors(async function runWithTaggedErrors(params) {
const runtime = await getRuntimeContext(params)
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',
},
}),
}
}
}
Demo
Demo code: https://github.com/dante01yoon/CVE-2025-29927
The scenario is as follows. The /admin page should not be visible to requests that do not include the auth cookie, so the middleware checks for the cookie.
Here is the middleware code.
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
// Check if the request is for the admin page
if (request.nextUrl.pathname.startsWith('/admin')) {
// Check for the authentication cookie
const isAuthenticated = request.cookies.has('auth');
if (!isAuthenticated) {
// Redirect to login page if not authenticated
return NextResponse.redirect(new URL('/login', request.url));
}
}
return NextResponse.next();
}
// Define which paths this middleware should run on
export const config = {
matcher: ['/admin/:path*'],
};
With the vulnerable 15.2.2 version, sending a request to /admin with a specific header value via curl will skip middleware and return the page.
x-middleware-subrequest: src/middleware:src/middleware:src/middleware:src/middleware:src/middleware
From 15.2.3 with the patch applied, the same request is redirected to /login.
Next.js versions affected by the vulnerability
Because most older versions are exposed, you should update Next.js quickly to address the vulnerability. The affected versions are:
-
Next.js 11.1.4 through 13.5.6
-
Next.js 14.x before 14.2.25
-
Next.js 15.x before 15.2.3
Applications not affected
If you do not use middleware or you host on Vercel/Netlify using their managed services, your application is not affected by this issue. However, many companies use self-hosting, so you should still patch by upgrading.
Alternatives to upgrading
There are cases where you cannot update due to compatibility issues.
- When self-hosting Next.js, put a web server such as Nginx in front of the Next.js server and return an error for requests that include the
x-middleware-subrequestheader.
server {
listen 80;
server_name yourdomain.com;
location / {
# Block requests containing the x-middleware-subrequest header
if ($http_x_middleware_subrequest != "") {
return 403;
}
# Proxy the request to your Next.js application
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}









