I love working with Next.js and have used it in projects since version 10 came out. The release of middleware in version 12 was exciting as it moved the framework closer to a full-stack framework.
Recently, a vulnerability in the middleware layer took the world by storm. The issue existed for around 3 years, back until version 12 of Next.js. With this post, I aim to summarize the vulnerability.
The vulnerability - CVE-2025-29927
Internally, Next.js uses a function that determines whether the middleware function should be run. The code was such that if a specific header is passed in with the request, the middleware would be skipped and the request would directly hit the server.
The header's name was x-middleware-subrequest
. The idea was to it as an internal header that would help identify if a middleware has been run before, and based on that prevent the triggering of infinite loops due to recursive requests.
This intentional implementation is a vulnerability specifically for apps where authentication logic relies solely on this middleware layer. Since, once a request gets past the middleware there is no further check.
Code breakdown
In the newer version of Next.js (before the vulnerability was fixed) the code looks like this:
While the complete implementation is not something I dived into: It seems like we are trying to limit the recursive depth of Next.js middleware invocation to 5. Once that happens, we will return with the response without running the rest of the logic.
It is even more apparent in one of the earlier versions. This is a code snippet from Dec 2021.
Here it seems we are simply skipping the middleware logic if the array of subrequests includes the middleware name. We execute the run function with the following parameters where the argument name
corresponds to the middleware name.
The header
So, it seems given the right value of this header we can prevent the logic of middleware from running. In Next.js, middlewares are supposed to be named middleware.ts
or middleware.js
. Depending on the version of Next.js, they might be placed in different sub-folders but even then a middleware's path can be guessed. That is why this vulnerability is easier to take advantage of.
The header x-middleware-subrequest
is supposed to be an internal header that is supposed to be set from the middleware and its value be modified at the middleware level itself. But by sending specific values in this header a middleware can be bypassed.
The exact value of this header is different for different versions of Next.js. For example:
For versions before 12.2: pages/_middleware
or pages/dashboard/_middleware
based on the route on which the middleware is placed.
For versions starting 12.2: middleware
For version starting 14: middleware:middleware:middleware:middleware:middleware
There are other nuances such as the existence of a src
folder, but the overall gist is that the value can be guessed in a few attempts.
Learnings
There is a lot to learn from this whole incident. Below are some of the key learnings I will take with me:
Do not use Next.js middlewares as the sole method for protecting the App. Even Next.js recommends against it. They do suggest using the middleware for optimistic redirecting, for example: missing cookie.
Every security issue should be treated as P0. Next.js team was notified of this issue in Feb, but at the time it seemed to only exist in older versions and that is why it was not acted upon immediately. Later it turned out to be an issue in even the newer versions of Next.js which have middleware.
Auth is hard. For production-grade apps, it does not make sense to reinvent the wheel.
Never trust the client. Technically anyone can manipulate request headers, and they should not be critical to such important logic.
Bonus
For folks interested in watching a demo of the video. Please watch it here: https://youtu.be/5j8aJiKrbgU
Note: For folks interested in understanding the complete issue, please visit this blog from Rachid A. which serves as an inspiration for this one too.