When building medium-to-large Next.js applications, we often need to perform some preprocessing before requests enter the application, such as user authentication, internationalization settings, and redirects. Starting with version 12, Next.js supports Middleware, which runs on edge nodes and can intercept and process requests before they reach page components.
This article will explain step by step how to implement the logic of multiple middlewares in Next.js while keeping the code clear and modular.

1. Basic Concepts of Middleware
In Next.js, you only need to create a file named middleware.ts
or in the project root directory middleware.js
to enable Middleware. This file will be automatically recognized by Next.js and no additional configuration is required.
A basic middleware looks like this:
// middleware.ts
import {NextResponse} from 'next/server';
import type {NextRequest} from 'next/server';
export function middleware(request: NextRequest) {
return NextResponse.next();
}
This middleware will be applied to all requests and simply allow the request to proceed.
2. Requirements for Implementing Multiple Middleware Logic
Let’s assume we have two separate functions:
authMiddleware
: Used to determine whether the user is logged in, if not logged in, redirect to/login
i18nMiddleware
: Used to set user language preference
We want them to be executed in sequence before each request. How can we do this?
3. Modular Middleware Logic
To keep the code clean, we split each functional module into a separate file:
middlewares/authMiddleware.ts
import {NextRequest, NextResponse} from 'next/server';
export function authMiddleware(request: NextRequest): NextResponse | void {
const isAuthenticated = true; // 示例逻辑
if (!isAuthenticated) {
return NextResponse.redirect(new URL('/login', request.url));
}
}
middlewares/i18nMiddleware.ts
import {NextRequest, NextResponse} from 'next/server';
export function i18nMiddleware(request: NextRequest): NextResponse | void {
const locale = request.headers.get('accept-language')?.split(',')[0] || 'en';
request.headers.set('x-locale', locale);
}
4. middleware.ts
Combining these middlewares in
// middleware.ts
import {NextRequest, NextResponse} from 'next/server';
import {authMiddleware} from './middlewares/authMiddleware';
import {i18nMiddleware} from './middlewares/i18nMiddleware';
export function middleware(request: NextRequest): NextResponse {
const authResponse = authMiddleware(request);
if (authResponse) return authResponse;
const i18nResponse = i18nMiddleware(request);
if (i18nResponse) return i18nResponse;
return NextResponse.next();
}
export const config = {
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};
We execute each middleware function in turn:
- If a middleware returns
NextResponse
, it means that it wants to intercept or terminate the request process, such as redirecting; - If it returns
undefined
, it means that it only modified the request status or header and did not affect the process, so it continued to execute the next one.
This mode is very intuitive and easy to debug and expand.
5. Why return
does using suspend subsequent middleware?
Many developers may be confused at first glance:
“Since I want to execute multiple middlewares, why does using return skip the subsequent logic?”
This is because we are implementing the processing logic of “chained middleware” – each middleware has the right to decide whether to terminate the request process.
For example, if the user is not authenticated, we want to redirect immediately /login
, so that other logic (such as setting the language, loading user data) does not need to be executed again.
This design idea is similar to the middleware model of many back-end frameworks such as Express.js.
VI. Summary
Next.js’s Middleware is a powerful and flexible tool that allows you to intercept and process logic early in the request lifecycle.
- You only need one
middleware.ts
file - Modularize each middleware function to facilitate reuse and testing
- Call them in order in the main middleware function and
NextResponse
control the flow based on whether they return
This design maintains a clear structure and facilitates future expansion. If you need to implement user permissions, internationalization, AB testing, and other features when building an application, Middleware is a mechanism worth using in depth.
I hope this article helps you understand and use Next.js Middleware!