← Back to chapters
11intermediate

Middleware

Run code before a request completes. Auth checks, redirects, rewrites, and geolocation.

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";
4
5export function middleware(request: NextRequest) {
6// Check if user is authenticated
7const token = request.cookies.get("session-token");
8
9// Protect dashboard routes
10if (request.nextUrl.pathname.startsWith("/dashboard")) {
11 if (!token) {
12 return NextResponse.redirect(new URL("/login", request.url));
13 }
14}
15
16// Add custom headers
17const response = NextResponse.next();
18response.headers.set("x-custom-header", "hello");
19return response;
20}
21
22// Only run middleware on these paths
23export 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";
3
4export function middleware(request: NextRequest) {
5const { pathname } = request.nextUrl;
6
7// 1. Redirect old URLs
8if (pathname === "/old-blog") {
9 return NextResponse.redirect(new URL("/blog", request.url));
10}
11
12// 2. Rewrite (URL stays same, content changes)
13if (pathname === "/docs") {
14 return NextResponse.rewrite(new URL("/docs/introduction", request.url));
15}
16
17// 3. Geolocation-based routing
18const country = request.geo?.country ?? "US";
19if (country === "IN" && !pathname.startsWith("/in")) {
20 return NextResponse.redirect(new URL(`/in${pathname}`, request.url));
21}
22
23// 4. Rate limiting headers
24const response = NextResponse.next();
25response.headers.set("X-RateLimit-Remaining", "99");
26return response;
27}
28
29export 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

ā–¶Watch and Learn