What middleware does
A single middleware.ts file at the project root runs before a request reaches any route — perfect for cross-cutting concerns like authentication checks, redirects, A/B testing, or localization.
// middleware.ts
import { NextRequest, NextResponse } from 'next/server';
export function middleware(request: NextRequest) {
const token = request.cookies.get('session')?.value;
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ['/dashboard/:path*', '/settings/:path*'], // only run on these paths
};
Common use cases
- Auth gating — redirect unauthenticated users away from protected routes before any page code runs.
- Redirects and rewrites — send old URLs to new ones, or serve different content for the same URL based on a cookie or header (A/B testing).
- Geolocation / localization — read
request.geoorAccept-Languageto redirect to a locale-specific path. - Adding headers — attach security headers or a request id to every response.
export function middleware(request: NextRequest) {
const response = NextResponse.next();
response.headers.set('x-request-id', crypto.randomUUID());
return response;
}
The Edge Runtime
Middleware runs on the Edge Runtime by default — a lightweight JavaScript runtime (not full Node.js) that executes close to the visitor's location on Vercel's edge network, so checks like auth redirects add minimal latency. Because it's not full Node.js, some Node-only APIs and large npm packages (like most database drivers) aren't available directly inside middleware — do the heavy lifting in a Server Component or Route Handler instead, and keep middleware focused on lightweight checks and redirects.
Middleware vs layout-level checks
Middleware runs for every matching request, even for static assets under that path, and before any React rendering starts — so it's the right layer for "should this request even proceed," while a layout's own logic (e.g. checking a user role to decide what to render) is better done inside a Server Component where you have full access to your data layer.