Run Code Before Every Matching Request
Middleware
A single `middleware.ts` at the project root runs on the Edge for the paths you match — perfect for auth checks, redirects, and rewrites.
What you'll learn
- Write a `middleware.ts` file
- Use the `config.matcher` to limit which paths it runs on
- Use `NextResponse.redirect`, `rewrite`, and `next`
Middleware runs on the Edge for every request that matches your config. It is the right place for cross-cutting concerns: auth gates, locale detection, A/B routing, rewrites.
A Minimal Auth Gate
// middleware.ts
import { NextRequest, NextResponse } from 'next/server'
export function middleware(req: NextRequest) {
const token = req.cookies.get('token')?.value
if (!token) {
const url = new URL('/login', req.url)
return NextResponse.redirect(url)
}
return NextResponse.next()
}
export const config = {
matcher: ['/app/:path*', '/account/:path*'],
} matcher patterns are checked before the function ever runs — anything outside them
skips middleware entirely, which is essential for performance.
Three Response Shapes
// Continue to the route as normal
return NextResponse.next()
// Bounce the browser to a new URL
return NextResponse.redirect(new URL('/login', req.url))
// Serve a different route under the same URL (no client redirect)
return NextResponse.rewrite(new URL('/maintenance', req.url)) rewrite is great for invisible routing — the URL stays the same but a different
component renders.
Edge Runtime Constraints
Middleware runs on the Edge Runtime — a V8 sandbox with Web Standards APIs (fetch,
Response, crypto.subtle) but no Node APIs (fs, child_process). Keep the work
light: middleware runs on every matching request.