Next.js middleware runs before a matched route completes, letting you redirect requests, rewrite URLs, or modify headers. You define it in a single middleware.ts file at the root of your project (or inside src/ if used).
Because middleware runs before pages and API handlers execute, it is ideal for fast session cookie checks, localization redirects, and response headers.
flowchart TD
A["Incoming Request"] --> B["middleware.ts"]
B --> C{"Session Cookie Present?"}
C -- "No" --> D["Redirect to /login"]
C -- "Yes" --> E["NextResponse.next()"]
E --> F["Render Page or Route Handler"]
middleware.tsExport a middleware function and a config.matcher array so the middleware runs only on targeted paths.
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const token = request.cookies.get('session_token')?.value;
const { pathname } = request.nextUrl;
if (pathname.startsWith('/dashboard') && !token) {
return NextResponse.redirect(new URL('/login', request.url));
}
if (pathname === '/login' && token) {
return NextResponse.redirect(new URL('/dashboard', request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ['/dashboard/:path*', '/login'],
};
Attach headers on NextResponse.next() to pass trace IDs downstream or apply security headers across matching routes.
// middleware.ts header snippet
const requestHeaders = new Headers(request.headers);
requestHeaders.set('x-request-id', crypto.randomUUID());
const response = NextResponse.next({
request: { headers: requestHeaders },
});
response.headers.set('x-frame-options', 'DENY');
return response;
matcherAlways specify a matcher filter. Without one, middleware intercepts every image, font, and _next/static chunk request, adding unnecessary latency.
pathname carefully before redirecting so /login does not redirect back to /login.middleware.ts be placed?Place middleware.ts at the project root next to package.json, or inside src/middleware.ts if your app/ folder lives inside src/.
middleware.ts files inside nested folders?No, Next.js allows only one root middleware.ts file; use pathname.startsWith(...) checks inside it to route logic by path.
NextResponse.redirect() and NextResponse.rewrite()?redirect() sends the user to a new URL visible in the browser bar, while rewrite() keeps the original URL and serves content from another path.
Sign in to track your learning journey, earn industry-recognized certificates, and join our elite developer community.
Quick Access With
You've completed this section! Take a quick 5-question quiz to check your understanding.