The Next.js middleware bypass (CVE-2025-29927): why one login check isn't enough
In 2025 a single header could skip Next.js middleware, and the login checks inside it. What happened and how to stop one failure opening every door.
By the Dignite Studios team · Nov 5, 2026 · 7 min read
Picture a building with a security guard at the front door and nothing but open plan inside. As long as the guard is there, it works. Then one day someone finds out that saying a particular phrase makes the guard walk off, and suddenly every room is open.
That, roughly, is what happened to a lot of Next.js apps in March 2025.
What actually happened
Next.js has a feature called middleware: a bit of code that runs before a request reaches your page. Teams use it for redirects, language routing and, very often, login checks. "If there's no session, send them to /login."
Inside the framework, middleware sometimes triggers more middleware, and Next.js uses an internal header called x-middleware-subrequest to keep track and avoid endless loops. The flaw, tracked as CVE-2025-29927, was that the framework trusted that header even when it came from an outside visitor. Send a request with the right value in it and Next.js decided middleware had already run, so it skipped it.
If your only protection for /admin lived in middleware, the protection simply didn't happen. No password guessing, no clever exploit chain. One extra header.
The fix shipped in patched versions across the supported lines, including 15.2.3, 14.2.25, 13.5.9 and 12.3.5. It was rated critical.
Who was actually at risk
- Self-hosted apps running the Node server (
next start, or the standalone output), where Next.js itself handles each request. - Apps relying on middleware for authorisation, not just cosmetic redirects.
- Not affected: static exports, which don't execute middleware, and apps on platforms that blocked the header before it reached your code.
How to check your own app
- Find your version. Run
npm ls next(or checkpackage.jsonand your lockfile). Compare it with the patched releases above. - Search for middleware. Look for a
middleware.tsormiddleware.jsfile in your project root orsrcfolder. No file, no exposure to this particular bug. - Read what it protects. If it's guarding dashboards, admin areas or API routes, those were open on any unpatched version.
- Check your logs. Look for the
x-middleware-subrequestheader in requests, if you record headers.
What to do about it
Upgrade
Move to a patched release of your line. It's the real fix. If you can't upgrade immediately, stop that header at the edge, before it reaches the app. In Nginx, for example:
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header x-middleware-subrequest "";
}Check authorisation where the data lives
This is the lesson that outlasts the bug. Middleware is a convenient first filter, but it shouldn't be the only one. Put the real check next to the thing being protected, so it holds even if something upstream is skipped:
export async function getInvoices() {
const session = await getSession(); // your own session helper
if (!session) throw new Error("Not signed in");
return db.invoice.findMany({ where: { userId: session.userId } });
}Now the invoices can't leave the database for someone who isn't signed in, whichever route they came by.
Test from the outside
Open a private window, so you're logged out, and visit your protected URLs directly. Try the API routes too. Doing this after every release takes two minutes and catches a surprising number of problems.
The bigger takeaway
Security people call this defence in depth, which is a grand name for a simple habit: don't let one thing be the only thing. One login check, one firewall rule or one plugin shouldn't be the sole reason your private data stays private. Ask of any protection you have, "What happens if this quietly stops working?"
Also notice how this was fixed. Keeping your framework current isn't glamorous, but it's the cheapest protection you'll ever buy. Our website and app security checklist has the rest of the routine, and if you'd like someone to test your protected pages from the outside, that's part of our audit.
Common questions
Was every Next.js site affected?
No. The problem hit self-hosted apps that ran with `next start` (or a standalone build) and used middleware for checks. Sites on some managed platforms were protected at the platform level, and fully static exports don't run middleware at all.
I've upgraded. Do I need to do anything else?
Upgrading closes this specific hole. The bigger fix is making sure your sensitive pages and data don't depend on middleware alone, so the next surprise can't expose them.
How do I know if my app was exploited?
Look in your access logs for requests carrying an `x-middleware-subrequest` header, and for visits to protected pages with no matching login. If your logs don't record headers, assume you can't be certain and review what was reachable.
Source: NVD: CVE-2025-29927
Not sure if your app has this problem?
Our $79 audit checks for issues like this and ranks what to fix first. No issues found? Full refund.
Get my audit · $79


