
Next.js middleware enables efficient request handling, authentication, redirects, and caching at the edge, improving performance, security, and user experience.
Table of content:
1. What is Middleware in Next.js
Middleware allows you to run code before a request is completed. Then, based on the incoming request, you can modify the response by rewriting, redirecting, modifying the request or response headers, or responding directly.
2. Setting Up Middleware in Next.js for Authentication to explain the flow of middleware.
Step 1: Use the file middleware.ts (or .js) in the root of your project to define Middleware. For example, at the same level as pages or app, or inside src if applicable.
Example: For app router having src folder, you can place middleware folder as follows

Step 2: Writing basic code in middleware
import { NextResponse } from "next/server"
import type { NextRequest } from "next/server"
// This function can be marked "async" if using "await" inside
export function middleware(request: NextRequest) {
return NextResponse.redirect(new URL('/home', request.url))
}
// "Matching Paths" (Description below)
export const config = {
matcher: '/about/:path*',
}
Matching Paths
Middleware will be invoked for every route in your project. Given this, it's crucial to use matchers to precisely target or exclude specific routes. This means you can define routes of web pages inside:
export const config = {
matcher: [“/dashboard”, “/auth/login”, “/auth/signup”],
}
// The above code means that the middleware will execute only when the pathname is “/dashboard”, “/auth/login” or “/auth/signup”.
Step 3: Setting Up Middleware in Next.js for Authentication and Explaining Its Flow Just paste the below code and read comments for understanding
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
// protectedRoutes are the routes that you want to protect from
// user who is not authenticated
const protectedRoutes = ['/students', 'clients'];
// Reverse of protected routes
const publicRoutes = ['/signin', '/signup'];
export async function middleware(request: NextRequest) {
// This will provide you the pathname of the browser route
const path = request.nextUrl.pathname;
// Returns true if the route is protected
const isProtectedRoute = protectedRoutes.some(route => path.includes(route));
// Returns true if the route is protected
const isPublicRoute = publicRoutes.some(route => path.includes(route));
// This will fetch token from the cookie
const token = request.cookies.get('token');
// If user donot have token inside cookie(User is not logged in or unauthenticated and
// the pathname is protected route than redirect user to “/auth/signin”)
if (!token && isProtectedRoute) {
return NextResponse.redirect(new URL('/auth/signin', request.url));
}
// If user is authenticated, route is public and is not equal to “/”, than redirect to “/”, this will help to hide public routes(“/auth/signin” , “/auth/signup”, etc.) to authenticated user.
if (token && isPublicRoute && path !== '/') {
return NextResponse.redirect(new URL('/', request.url));
}
// Default return:
return NextResponse.next();
}
// Because of the matcher written below the middleware will run for every file except API routes, Next.js static assets(/_next/static), Image optimization routes(/_next/image), pngs, and similar asserts.
export const config = {
matcher: ['/((?!api|_next/static|_next/image|.*\\.png$).*)'],
};
// You can also specify each path as shown above.