Middleware
One file, project-wide
middleware.ts (or .js) file at the root of your project — next to app/, not inside it (or inside src/ if you're using that layout). You export a function named middleware, which Next.js runs for every matching request before routing decides which page or handler to invoke.middleware.ts
import { NextRequest, NextResponse } from 'next/server'
export function middleware(request: NextRequest) {
const isLoggedIn = request.cookies.has('session')
if (!isLoggedIn && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url))
}
return NextResponse.next()
}The three responses a middleware can return
Method | Effect |
|---|---|
| Let the request continue to the matched page/route as normal (optionally with modified request headers) |
| Send the browser an HTTP redirect to a different URL — the browser address bar changes |
| Serve content from a different internal path while keeping the URL the browser sees unchanged |
example.com/pricing, but you quietly serve them /pricing-variant-b based on a cookie.middleware.ts — rewrite example
import { NextRequest, NextResponse } from 'next/server'
export function middleware(request: NextRequest) {
const variant = request.cookies.get('ab-variant')?.value ?? 'a'
if (request.nextUrl.pathname === '/pricing' && variant === 'b') {
return NextResponse.rewrite(new URL('/pricing-variant-b', request.url))
}
return NextResponse.next()
}Reading and setting cookies
NextRequest and NextResponse both expose a convenient .cookies API, so middleware can read incoming cookies and set new ones on the outgoing response without touching the raw Set-Cookie header format.middleware.ts — setting a cookie
import { NextRequest, NextResponse } from 'next/server'
export function middleware(request: NextRequest) {
const response = NextResponse.next()
if (!request.cookies.has('visitor-id')) {
response.cookies.set('visitor-id', crypto.randomUUID(), {
httpOnly: true,
maxAge: 60 * 60 * 24 * 365,
})
}
return response
}middleware.ts. Keep middleware limited to lightweight checks (cookies, headers, URL logic) and do heavier data access in the page or Route Handler it forwards to.matcher config that scopes which paths middleware actually runs on.Middleware runs before a request reaches a page, layout, or Route Handler, from a single
middleware.tsat the project root.It exports a
middleware(request: NextRequest)function that returnsNextResponse.next(),.redirect(), or.rewrite().Rewrites change what's served without changing the URL the browser shows; redirects change the URL itself.
request.cookies/response.cookiesprovide a friendly API for reading and setting cookies.Middleware runs on the Edge Runtime by default, so it should stay lightweight and avoid Node-only APIs.