Technology · Next.js
Next.js Middleware
Use Next.js middleware for redirects, auth checks, headers, and request rewriting in the App Router.
TL;DR
- 01Create a middleware.ts file at the project root to intercept requests.
- 02Use middleware to check auth, redirect, or modify headers.
- 03Return NextRequest or NextResponse to control the request flow.
Basic Setup
- Create a
middleware.tsfile at the root of your project.// middleware.ts import { NextRequest, NextResponse } from "next/server"; export function middleware(request: NextRequest) { return NextResponse.next(); } - Middleware runs on every request before it reaches your routes.
- Use the
matcherexport to specify which paths trigger the middleware.export const config = { matcher: ['/api/:path*', '/admin/:path*'] }; - Return
NextResponse.next()to continue to the next handler. - Middleware runs in a Node.js runtime, not the browser.
Authentication Checks
- Check for a token in cookies and redirect if missing.
export function middleware(request: NextRequest) { const token = request.cookies.get('auth')?.value; if (!token && request.nextUrl.pathname.startsWith('/admin')) { return NextResponse.redirect(new URL('/login', request.url)); } return NextResponse.next(); } - Verify JWT tokens or check session validity before route access.
- Redirect unauthenticated users to a login page automatically.
- Use middleware to protect entire route segments without code in each page.
- Combine with headers to check authentication across the app.
Redirects and Rewrites
- Redirect users to a different URL using
NextResponse.redirect().if (request.nextUrl.pathname === '/old-page') { return NextResponse.redirect(new URL('/new-page', request.url)); } - Rewrite the request internally without changing the user's URL.
if (request.nextUrl.pathname.startsWith('/api')) { return NextResponse.rewrite(new URL('/api-v2/handler', request.url)); } - Use redirects for public URL changes and SEO-friendly migrations.
- Use rewrites to hide internal routing or proxy external APIs.
- Both preserve the original URL in the browser when rewriting.
Custom Headers
- Add or modify headers for every request or response.
export function middleware(request: NextRequest) { const response = NextResponse.next(); response.headers.set('X-Custom-Header', 'value'); return response; } - Add security headers like CORS, CSP, or rate-limiting headers.
- Read incoming headers to customize behavior based on the request.
const userAgent = request.headers.get('user-agent'); - Middleware runs before routes see the request, so headers apply everywhere.
- Use response headers to control caching and security policies.
Common Patterns
- Check locale or country from the request and redirect to the right version.
const locale = request.headers.get('accept-language')?.split('-')[0]; if (locale === 'es') { return NextResponse.redirect(new URL('/es' + request.nextUrl.pathname, request.url)); } - Log requests and track analytics in middleware before routes handle them.
console.log(`${request.method} ${request.nextUrl.pathname}`); - Rate limit requests based on IP address or user ID.
const ip = request.ip || 'unknown'; // Check if over limit... - Combine multiple checks in sequence for layered security.
- Middleware executes synchronously, so keep operations fast.
Tip: Use the
matcherconfig to narrow which routes trigger middleware, since it runs before every request and impacts performance.
Warning: Middleware runs for every request, so expensive operations will slow your app down — keep logic fast and simple.