Middleware
This page explains how to implement and use middleware in Next.js.
Understanding middleware
Middleware in Next.js is a powerful feature that lets you intercept and control the flow of requests and responses throughout your application. It runs before a request is completed, allowing you to modify the response based on the incoming request.
Middleware can be used for:
- Redirecting users
- Rewriting URLs
- Modifying request or response headers
- Setting or reading cookies
- Authentication and authorization
- A/B testing
Creating middleware
To create middleware, add a middleware.js
or middleware.ts
file in your project’s root or src
directory:
import { NextResponse } from "next/server";import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) { // Your middleware logic here return NextResponse.next();}
Specifying paths for middleware
You can specify which paths the middleware should run on using two approaches:
1. Using the matcher config
import { NextResponse } from "next/server";import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) { return NextResponse.redirect(new URL("/", request.url));}
export const config = { matcher: "/profile",};
This middleware will only run when the /profile
path is accessed.
2. Using conditional statements
import { NextResponse } from "next/server";import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) { if (request.nextUrl.pathname === "/profile") { return NextResponse.redirect(new URL("/", request.nextUrl)); }}
This approach gives you more flexibility to apply different logic based on the request path.
Redirecting users
You can redirect users from one URL to another:
import { NextResponse } from "next/server";import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) { // Redirect /profile to the home page if (request.nextUrl.pathname === "/profile") { return NextResponse.redirect(new URL("/", request.nextUrl)); }}
URL rewrites
Rewrites allow you to serve content from a different URL while keeping the original URL in the browser:
import { NextResponse } from "next/server";import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) { // Rewrite /profile to /hello if (request.nextUrl.pathname === "/profile") { return NextResponse.rewrite(new URL("/hello", request.nextUrl)); }}
With this middleware, when a user visits /profile
, they’ll see the content from /hello
, but the URL in the browser will remain /profile
.
Working with cookies
You can read and set cookies in middleware:
import { NextResponse } from "next/server";import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) { // Get the response that would normally be returned const response = NextResponse.next();
// Check if the user has a theme preference const themePreference = request.cookies.get("theme");
// If no theme preference is set, default to dark if (!themePreference) { response.cookies.set("theme", "dark"); }
return response;}
Modifying headers
You can add or modify headers in the response:
import { NextResponse } from "next/server";import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) { // Get the response const response = NextResponse.next();
// Set a custom header response.headers.set("custom-header", "custom-value");
return response;}
Combining multiple middleware features
You can combine multiple middleware features in a single implementation:
import { NextResponse } from "next/server";import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) { // For the profile page, redirect to login if not authenticated if (request.nextUrl.pathname === "/profile") { const authCookie = request.cookies.get("authenticated");
if (!authCookie || authCookie.value !== "true") { return NextResponse.redirect(new URL("/login", request.nextUrl)); } }
// For all requests, set a custom header const response = NextResponse.next(); response.headers.set("x-app-version", "1.0.0");
return response;}
export const config = { matcher: ["/profile", "/dashboard/:path*"],};
Good to know
- Middleware runs before cached content is served
- The matcher config supports path matching, negative lookaheads, and regular expressions
- You can only have one middleware file in your project
- Middleware doesn’t run for static assets in the public directory
- For complex path matching, use the matcher config instead of conditional statements