Middleware (middleware.ts)
Middleware (middleware.ts)
Level 10 — Advanced Architecture A single file sitting at the very edge of your application that intercepts every incoming HTTP request before it reaches your routes or pages, used primarily for redirects, rewrites, and lightweight authentication.
1. Prerequisites
- Middleware vs Route Handlers — The conceptual difference between these two systems.
NextRequest&NextResponse— The core API used within this file.- Authentication & Session Management — The primary purpose of route checkpointing.
2. Term Category
Security & Middleware (Global Server Request Interceptor): Middleware (middleware.ts) intercepts incoming server HTTP requests before routing, enforcing authentication, redirects, and headers.
3. Explanation
Environment Context
- Edge Runtime ONLY
(1) Design Motivation — "Why did we design this?"
If you want to support internationalization (/en/about vs /fr/about), you need to check the user's Accept-Language browser header and redirect them to the correct subfolder.
If you want to protect your dashboard, you need to check if they have a valid session cookie before loading the /dashboard UI.
Doing this on a page-by-page basis is tedious and slow because the Node.js server has to fully boot up the React component tree just to realize it needs to redirect the user.
Middleware runs on the ultra-fast Edge Runtime. It intercepts the request globally, checks the headers/cookies, and can instantly redirect the user before the Node.js server even knows the request happened.
(2) The Syntax
You create a single file named middleware.ts at the root of your project (or inside src/ if you use it).
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
// 1. Check for an auth cookie
const token = request.cookies.get('auth_token');
// 2. If the user is trying to access /admin but has no token...
if (request.nextUrl.pathname.startsWith('/admin') && !token) {
// 3. Immediately redirect them to login!
return NextResponse.redirect(new URL('/login', request.url));
}
// 4. Otherwise, let the request pass through to the page normally.
return NextResponse.next();
}
(3) The Config Matcher
By default, middleware.ts runs on every single request—including requests for images (/logo.png), CSS files, and Next.js internal files. This is terrible for performance.
You should ALWAYS export a config object with a matcher array to limit which routes the Middleware applies to.
export const config = {
// Only run Middleware on routes starting with /admin or /dashboard
matcher: ['/admin/:path*', '/dashboard/:path*'],
};
4. Common Mistakes & Pitfalls
Mistake 1: Trying to use Node.js Modules
The mistake: A developer imports bcrypt to verify a password hash inside the middleware.ts file.
Why it's wrong: Middleware strictly runs on the Edge Runtime (like Cloudflare Workers). It is NOT a Node.js environment. Any library that relies on Node.js native modules (like fs, crypto, bcrypt, or heavy ORMs like Prisma) will crash the app.
Golden Rule: Middleware must be incredibly lightweight. If you need to do heavy cryptography or database lookups, you must do it in a Route Handler or Server Component, NOT in Middleware. Use lightweight web standard APIs (like crypto.subtle for JWT verification).
Mistake 2: Placing middleware.ts inside Sub-Folders Instead of Project Root or src/
The mistake: Creating app/middleware.ts or app/dashboard/middleware.ts.
Why it's wrong: Next.js recognizes ONLY a SINGLE middleware.ts file located at the project ROOT directory (or inside src/). Placing it inside app/ causes Next.js to ignore it.
Incorrect:
// app/middleware.ts ❌ Ignored by Next.js compiler!
Fix:
// middleware.ts (Root directory or src/middleware.ts)
Mistake 3: Omitting config.matcher Resulting in Middleware Execution on Static Assets
The mistake: Writing middleware.ts without configuring config.matcher.
Why it's wrong: Without a matcher filter, middleware executes on EVERY request, including static images (.png), favicon, and _next/static JS chunks, degrading site performance.
Incorrect:
// Missing config.matcher ❌ Executes on all static assets!
Fix:
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)']
};
5. Practice Exercises
Exercise 1: Authoring Centralized Server Middleware
Scenario:
Create middleware.ts to log incoming request paths and attach a custom response header X-Request-Time.
Requirements:
- Export
middleware(req: NextRequest)in project rootmiddleware.ts.
Answer
Implementation
// middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function middleware(req: NextRequest) {
const res = NextResponse.next();
res.headers.set("X-Request-Time", Date.now().toString());
console.log(`[Middleware] ${req.method} ${req.nextUrl.pathname}`);
return res;
}
Technical Explanation
middleware.tsplaced at the project root executes on every incoming server request before page/route resolution.NextResponse.next()allows the request to continue to downstream page handlers while attaching custom response headers.- Central entry point for server request interception.
Exercise 2: Filtering Middleware Execution with config.matcher
Scenario:
Restrict middleware.ts execution strictly to /dashboard/** and /api/protected/** paths using config.matcher.
Requirements:
- Export
const config = { matcher: [...] }.
Answer
Implementation
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function middleware(req: NextRequest) {
return NextResponse.next();
}
export const config = {
matcher: ["/dashboard/:path*", "/api/protected/:path*"]
};
Technical Explanation
config.matcherfilters which URL paths trigger middleware execution.- Bypasses middleware execution for static assets (
/_next/static, images, favicons). - Essential performance optimization to avoid unnecessary middleware runs on static files.
Exercise 3: Performing Conditional Redirects in Middleware
Scenario:
Redirect users attempting to access /admin without a role=admin cookie to /unauthorized.
Requirements:
- Check
req.cookies.get('role')and callNextResponse.redirect().
Answer
Implementation
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function middleware(req: NextRequest) {
const role = req.cookies.get("role")?.value;
if (req.nextUrl.pathname.startsWith("/admin") && role !== "admin") {
return NextResponse.redirect(new URL("/unauthorized", req.url));
}
}
Technical Explanation
NextResponse.redirect()issues an immediate HTTP 307 temporary redirect response.- Intercepts unauthorized requests before server rendering or database querying begins.
- High performance server security guard.
6. Related Terms
- Edge Runtime vs Node.js Runtime — The restricted environment where Middleware runs.
NextRequest&NextResponse— The object used to trigger the redirects and rewrites.cookies()andheaders()fromnext/headers— Related concept:cookies()andheaders()fromnext/headers.- Middleware vs Route Handlers — Related concept: Middleware vs Route Handlers.
- Authentication & Session Management — Related concept: Authentication & Session Management.
- Internationalization (i18n) — Related concept: Internationalization (i18n).
- Route Handlers (
route.ts) — Route Handlers.
7. Key Takeaways
- Middleware (
middleware.ts) is a single, global interceptor for incoming HTTP requests. - It is primarily used for Authentication checks, Internationalization routing, Redirects, and Rewrites.
- It runs on the Edge Runtime, meaning it is ultra-fast but CANNOT use Node.js modules or databases.
- You must export a
config.matcherto prevent Middleware from running on static assets like images and CSS.