Understanding Next.js Middleware
Middleware in Next.js is a powerful feature that sits in the request-response pipeline immediately after the next.config.js
is processed. It allows developers to intercept requests before they reach route handlers or pages, enabling actions like rejection, redirection, rewriting, or modifying requests.
How to Create a Middleware
To add middleware, create a middleware.js
or middleware.ts
file in the src
folder. The middleware function handles the request and must return a NextResponse
. Here's the simplest middleware example:
import { NextResponse, type NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
return NextResponse.next(); // Passes the request to the next handler
}
This basic middleware forwards requests to the next level in the pipeline, whether that’s a page, a static file, or a route handler.
Filtering Requests with Matchers
By default, middleware processes all requests, including static files. To control which requests are handled, export a config
object with a matcher
property:
export const config = {
matcher: ['/admin/:path*'], // Matches all requests starting with /admin
};
You can also exclude requests by using regular expressions:
export const config = {
matcher: ['/(?!.*\\.(css|js|ico|png|jpg|jpeg|gif|svg)$).*'],
};
This example excludes requests for common static assets.
Enhanced Request Object
The request parameter in middleware is an enhanced version of the Fetch API's Request
object. It includes additional properties, such as nextUrl
, which simplifies operations like query parsing and path analysis.
Example:
import { NextResponse, type NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
console.log(request.nextUrl.pathname); // Logs the requested path
return NextResponse.next();
}
Redirects and Authorization
Middleware can handle authentication and authorization by inspecting requests. Here's an example of redirecting unauthorized users:
export function middleware(request) {
const token = request.headers.get('authorization');
if (!token || token !== '1234') {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
This example redirects requests to /admin
to a login page if the authorization token is invalid or missing.
Setting Headers and Cookies
Middleware can modify response headers and set cookies before forwarding the response:
import { NextResponse } from 'next/server';
export function middleware(request) {
const response = NextResponse.next();
response.headers.set('X-Custom-Header', 'Hello');
response.cookies.set('myCookie', '123');
return response;
}
Logging Requests
Middleware is an ideal place for logging. For instance, you can log all incoming requests:
export function middleware(request) {
console.log(request.nextUrl.href);
return NextResponse.next();
}
This is especially useful for analytics or monitoring purposes, but avoid adding complex logic as it can impact performance.
Dynamic Redirects
Middleware supports dynamic redirects based on request conditions, such as path matching or headers:
export function middleware(request) {
const oldUrls = {
'/old-page': '/new-page',
'/legacy': '/modern',
};
const newPath = oldUrls[request.nextUrl.pathname];
if (newPath) {
return NextResponse.redirect(new URL(newPath, request.url));
}
return NextResponse.next();
}
JSON and Custom Responses
Middleware can return JSON responses or set specific HTTP status codes:
import { NextResponse } from 'next/server';
export function middleware(request) {
return new NextResponse(JSON.stringify({ message: 'Unauthorized' }), {
status: 401,
headers: { 'Content-Type': 'application/json' },
});
}