Published on July 6, 2025
Middleware is a Next.js feature that lets you inspect a request that a user has made to your server and perform logic before it hits your route handler. It's used for things like authentication, redirection, and logging.
This post explains what Next.js Middleware is and what its best use cases are. It also includes some practical examples of how to set it up and use it effectively.
Next.js Middleware is a processing layer that allows you to intercept a request on its way to your application's route handler and run some code. It enables you to inspect and modify the request before it completes.
Middleware runs server-side, so doesn't affect the frontend bundle size. It uses the Edge Runtime, which enables faster response times by deploying globally, closer to the user's geographical location. Alternatively, it can be self-hosted. Middleware can be helpful in many ways; for example, it can detect a user’s language by checking the request headers and redirect them to the correct language version of your site.
Middleware is available in Next.js version 12 (released in 2021) and above.
Next.js Middleware runs in the Edge Runtime rather than the standard Node.js runtime. The Edge runtime is optimized for performance and speed and can be globally distributed on some hosting platforms. It uses Web APIs (Request, Response) but doesn't have access to Node.js’s built-in modules such as fs
or path
, cannot read the request body (only headers, URL, cookies, etc.), and is stateless (it cannot retain any server state between requests).
Middleware can be deployed in various environments:
Platform | Edge Support | Notes |
---|---|---|
Vercel | Yes | First-class support with global Edge Network |
Netlify | Yes | Supported via Edge Functions |
Docker | Partial | Requires you to self-host with a custom config |
AWS Lambda | Partial | Not optimized for the Edge. More suited to API routes |
You don't have to host on a platform that supports the Edge Runtime to use Next.js Middleware. You can still deploy your app using next start
and host it in a regular Node.js environment.
However, Next.js Middleware running in a regular Node.js environment still has the same limitations described above. If you require full Node.js capabilities in your Middleware, you can configure it to use the full Node.js runtime in your self-hosting environment.
When a user makes a request to your Next.js app, Middleware is the first layer of logic that runs in the request lifecycle before any of the routing decisions are made. By default, it is applied globally across all routes, but you can target it at specific route paths with matchers.
Here's what happens with a typical request when Next.js Middleware is implemented:
The user request hits the Edge Middleware (lightweight JavaScript environment).
Middleware code can inspect the request (headers, cookies, path, IP) and then modify, redirect, rewrite headers, or just allow it to proceed.
After Next.js Middleware runs, the request is forwarded to the Next.js routing layer, which attempts to match the request to one of the following, depending on how you have Next.js configured to handle routing:
A page route from the filesystem or app directory.
A dynamic route (/userProfile/[userId]
).
A static file.
Once the route is matched and handled, the response is generated and the HTML or API response is sent to the user's browser.
If your Next.js app is unable to find a matching route, then a fallback or error page will be displayed.
The Middleware code will run for every route in your project by default. You can optionally use matchers to target certain routes where you want the Middleware to run.
While it might seem simpler to apply Middleware globally, running Middleware for every request could lead to performance issues. On top of that, by using matchers, you make your code more maintainable, since the code is easier to understand (clear to see which routes Middleware runs for, and therefore, its purpose).
There are two ways to define which paths Next.js Middleware will run on:
A custom matcher config, which can be defined at the bottom of the Middleware file:
You can configure it to target just one path or multiple paths in an array. The above code applies the Middleware to all routes under profile
and dashboard
and ignores the rest. The :path*
wildcard covers all nested routes.
A conditional statement inside of the Middleware function, which allows the Middleware to run globally, but you decide at runtime whether to act on the request (depending on the route):
NextResponse is a utility class from the next/server
module that extends the WebResponse API. It was made to be used in Middleware and Edge functions, giving you full control over how to respond to incoming requests. If you are familiar with Express, then it's the equivalent of res
and next()
. In the code above, it is used to allow the request to proceed with NextResponse.next()
, but it can also be used to redirect, rewrite, or set headers and cookies.
You can use Next.js Middleware for authentication (confirm user's identity) and authorization (control what a user can access). Middleware allows you to check cookies or auth tokens before a request reaches a protected route (such as a dashboard or profile), redirect unauthenticated users based on the authentication status, and enforce role-based access control (only admin users can access /admin
).
See the code example later in this article that demonstrates how Middleware can interact with authentication code.
There are certain use cases Next.js Middleware is optimized for and other tasks it doesn't perform so well on. Below is a list of what you should aim to use it for:
Middleware can handle localization and internationalization by detecting a user's locale from cookies or HTTP headers. Based on localization and other factors, it can then conditionally redirect to a different route (for example, /en
, /es
routes for different languages), modify headers, and dynamically rewrite URLs.
You can use Middleware to read or set cookies to manage user sessions, preferences, or feature flags for feature gating, which enables you to conditionally enable or disable features for specific users.
You can use Middleware to capture request headers, IP addresses, or geolocation data before they reach your route logic. You can then log this data for audit trails, basic analytics, or security monitoring.
Middleware can inspect incoming requests before they hit your routes, making it the perfect place to implement basic rate limiting to prevent bots or malicious users from abusing your resources.
You can use attributes like IP addresses, headers, or the user agent string to:
Throttle requests from clients that make too many requests too quickly.
Identify and redirect suspicious traffic.
Block known bots or scrapers.
This allows you to filter suspicious traffic before it reaches the resource-intensive parts of your code, such as those that make API requests or database queries. For a backend-heavy application, this would act as a first line of defense against malicious traffic, but for JAMstack or static applications deployed on platforms that support the Edge Runtime, this Middleware may be the only layer of protection for the app.
Running Middleware on every route indiscriminately increases latency and resource usage. You can solve this by using a matcher to scope Middleware properly so it only runs when it needs to.
You shouldn't use Middleware for heavy computation or blocking operations. Middleware should be lightweight and fast — doing expensive calculations will delay every request, reversing any performance benefits.
Middleware also has no persistent memory, and, out of the box, there is no way to manage state or sessions. It also can't handle database queries, as it runs in Edge Runtime where there is no support for core Node.js modules. There are workarounds — for example, you could proxy database queries through an external API. However, this is generally not encouraged; Middleware is designed for lightweight, stateless request handling.
You can clone the full code repo to run these code examples and see them in action.
Here are some examples that demonstrate how to use Next.js Middleware, from basic setup to advanced usage.
To get started, install Node.js and then bootstrap a new Next.js project by running:
npx create-next-app@latest middleware-examples
Note that you can replace middleware-examples
with a project name of your choice.
Choose the following options during the setup wizard:
Then, navigate into your new project and start the dev server:
cd middleware-examples
npm run dev
Create a middleware.js
file in the root of your project. Next.js only supports one Middleware file per project, and this file acts as the entry point for all Middleware logic.
If you will be writing a lot of Middleware code, you can keep your code modular by splitting it into multiple files and importing them into the main middleware.js
file. In these examples, all Middleware functions will be placed in a middleware
folder in the root of the project and then imported into middleware.js
from there.
The below Middleware example shows how to create a basic logging Middleware function. Create a logger.js
file in the middleware
folder with the following code:
Then add the following code to the main middleware.js
file to import The logRequest
function and use it:
Now that this is set up, you should be able to see each request logged in the console, as shown below:
As this Middleware does not use a matcher, it runs for every request. You can add a matcher that applies to all Middleware by adding the following code to the bottom of the middleware.js
file:
Now the Middleware will only run for the root URL.
You can inspect cookies inside the Middleware to perform tasks like checking for auth tokens and redirecting if not found.
To demonstrate this, first add some test routes for a dashboard and a login page (which requires the creation of some React components) by creating folders under the app directory with a page.jsx
file in each:
And then:
Currently, both of these pages are visible to any visitor. Add authentication by creating an auth.js
file in the middleware
folder:
The above function inspects the cookie to see if the auth-token
is present and then returns a boolean. This is intentionally simple for demonstration purposes and doesn't check whether it's secure or valid.
As before, you need to add this to the main middleware.js
file to use it:
Now when you try to access the dashboard, it should immediately redirect you to the login page.
You can set headers with Next.js Middleware, allowing you to do things like add feature flags, which can define what functionality is available to the user.
The example below demonstrates this by using Middleware to add a feature flag that decides whether to show a new feature in the dashboard (in the case of this example, a message). Create a new file called setFeatureHeader.js
in the middleware
directory and add the following code:
Modify the main middleware.js
file as below:
Then update the code in dashboard/page.jsx
to the following:
This React component now gets a list of the headers, finds the x-feature-header
, creates a boolean variable by checking if it's "new," and then uses it in a conditional to decide whether to show the new feature or not.
This section will explore the advanced capabilities of Next.js Middleware.
Next.js lacks built-in global CORS handling for API routes. Middleware offers a perfect place to set CORS headers for trusted domains:
The above adds the CORS headers to allow access to the app from https://cdn.contentful.com
, this enables cross-origin access for GET
, POST
, and OPTIONS
requests.
NextFetchEvent.waitUntil()
allows you to run asynchronous background tasks after returning a response. This is useful for tasks like logging or sending analytics where it may take some time to complete:
Next.js Middleware allows you to inspect the request data so you can conditionally change the behavior of your application. Here are some flags you can use:
request.headers.get("user-agent")
to detect a user agent or language for language-based routing.
request.cookies.get("auth-token")
to determine the login state or enable feature toggles.
request.geo.country
to serve location-specific content.
request.nextUrl.searchParams.get('preview')
to toggle features via URL flags.
Next.js is a highly flexible React framework. You can combine various tools like Next.js Middleware, static site generation (SSG), server-side rendering (SSR), and Edge functions to provide performant, personalized, and localized experiences.
With these features, Next.js can be used as part of a composable architecture. Using your business logic and executing Middleware functions at the edge, you can create unique and personalized experiences combined with Contentful for content creation, curation, and management.
You can get started with Next.js using the Contentful starter. Contentful empowers you to create and manage text, images, videos, and more. You can request this content from your Next.js apps via REST or GraphQL, all delivered via a global, high-speed CDN and available for future use cases, including digital billboards, AI assistants, and mobile apps.
Subscribe for updates
Build better digital experiences with Contentful updates direct to your inbox.