Middleware: Intercept Every Request
Middleware runs BEFORE your route handles the request. It's perfect for auth checks, redirects, A/B testing, geolocation-based routing, and request/response modification. It runs on the Edge runtime (fast, everywhere).
Creating Middleware
src/middleware.ts
tsx
1// src/middleware.ts (must be at src root, not in app/)2import { NextResponse } from "next/server";3import type { NextRequest } from "next/server";45export function middleware(request: NextRequest) {6// Check if user is authenticated7const token = request.cookies.get("session-token");89// Protect dashboard routes10if (request.nextUrl.pathname.startsWith("/dashboard")) {11 if (!token) {12 return NextResponse.redirect(new URL("/login", request.url));13 }14}1516// Add custom headers17const response = NextResponse.next();18response.headers.set("x-custom-header", "hello");19return response;20}2122// Only run middleware on these paths23export const config = {24matcher: ["/dashboard/:path*", "/api/:path*"],25};ā ļø
File location matters
middleware.ts MUST be at the root of your src/ directory (or project root if no src/). NOT inside app/. If you put it in the wrong place, it won't run.
Common Middleware Patterns
src/middleware.ts
tsx
1import { NextResponse } from "next/server";2import type { NextRequest } from "next/server";34export function middleware(request: NextRequest) {5const { pathname } = request.nextUrl;67// 1. Redirect old URLs8if (pathname === "/old-blog") {9 return NextResponse.redirect(new URL("/blog", request.url));10}1112// 2. Rewrite (URL stays same, content changes)13if (pathname === "/docs") {14 return NextResponse.rewrite(new URL("/docs/introduction", request.url));15}1617// 3. Geolocation-based routing18const country = request.geo?.country ?? "US";19if (country === "IN" && !pathname.startsWith("/in")) {20 return NextResponse.redirect(new URL(`/in${pathname}`, request.url));21}2223// 4. Rate limiting headers24const response = NextResponse.next();25response.headers.set("X-RateLimit-Remaining", "99");26return response;27}2829export const config = {30matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],31};š
Keep middleware lean
Middleware runs on EVERY matched request. Keep it fast.
⢠Don't do database queries here
⢠Don't do heavy computation
⢠Use it for quick checks: is there a cookie? redirect? rewrite?
⢠Heavy auth logic belongs in your route/page
⢠Don't do database queries here
⢠Don't do heavy computation
⢠Use it for quick checks: is there a cookie? redirect? rewrite?
⢠Heavy auth logic belongs in your route/page