Middleware

Master Next.js Middleware — the edge function that runs before every request, enabling auth redirects, A/B testing, geolocation, and request modification at the CDN edge.

Last reviewed: January 2025

·Next.js 12+Next.js 13Next.js 14Next.js 15
✅
Quick Understanding: Middleware is a single middleware.ts file at the project root that runs before every matched request. It runs on the Edge Runtime — globally distributed, extremely fast. Use it for auth redirects, header injection, A/B testing, and geolocation-based routing.

🔍 What Is It?

Next.js Middleware is a function exported from middleware.ts at the project root:

  • →Runs on every request before it reaches a page or Route Handler
  • →Edge Runtime — runs at CDN edge nodes globally (not Node.js)
  • →Can read and modify request headers, cookies, and URL
  • →Can redirect or rewrite requests
  • →Cannot access the filesystem, Node.js APIs, or most npm packages
  • →Matcher config controls which routes trigger middleware

🎯 Why Do We Need It?

Middleware solves problems that cannot be solved in layouts or pages:

  • →Fast auth redirects — redirect unauthenticated users before the page even loads
  • →A/B testing — rewrite requests to different pages based on a cookie
  • →Geolocation — redirect users to regional versions based on their country
  • →Request enrichment — add user ID to headers so all downstream components can read it
  • →Bot protection — block or challenge suspicious requests at the edge

🏗️ What Happens in Real Projects?

Middleware in production:

  • →Auth: verify JWT, redirect to /login if invalid
  • →Internationalisation: detect locale, rewrite to /en/ or /fr/
  • →Feature flags: rewrite to /beta/ for users with the beta cookie
  • →Rate limiting: count requests per IP at the edge
  • →Security headers: add CSP, HSTS, X-Frame-Options to all responses

⚙️ How It Works

Middleware execution flow:

  1. Request arrives at CDN edge
  2. Middleware function runs (Edge Runtime, ~1ms)
  3. Middleware can: redirect, rewrite, modify headers, or pass through
  4. If passing through: request continues to Next.js server
  5. Server Component / Route Handler runs

The matcher config tells Next.js which paths trigger middleware. Without a matcher, middleware runs on EVERY request — including static files and images, which is expensive.

💻 Example

Auth Middleware and Request Enrichment — Production middleware for auth protection and request header enrichment.

ts
// middleware.ts (project root)
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { verifyJWT } from '@/lib/auth-edge'; // Must be edge-compatible

export async function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;

  // Get session token from cookie
  const token = request.cookies.get('session')?.value;

  // Verify token
  const session = token ? await verifyJWT(token) : null;

  // Protect dashboard routes
  if (pathname.startsWith('/dashboard') || pathname.startsWith('/settings')) {
    if (!session) {
      const loginUrl = new URL('/login', request.url);
      loginUrl.searchParams.set('callbackUrl', pathname);
      return NextResponse.redirect(loginUrl);
    }
  }

  // Redirect authenticated users away from auth pages
  if (pathname.startsWith('/login') || pathname.startsWith('/register')) {
    if (session) {
      return NextResponse.redirect(new URL('/dashboard', request.url));
    }
  }

  // Pass through with user ID in header (available in Server Components)
  const response = NextResponse.next();
  if (session) {
    response.headers.set('x-user-id', session.userId);
  }
  return response;
}

// IMPORTANT: Always add a matcher to avoid running on static files
export const config = {
  matcher: [
    // Match all paths EXCEPT:
    '/((?!_next/static|_next/image|favicon.ico|.*\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
  ],
};

🤔 Common Confusions

❓ Can middleware access the database?

No — middleware runs on the Edge Runtime, which does not support Node.js APIs including most database drivers. For auth, use JWT verification (edge-compatible) rather than database session lookups. If you need DB access, use a lightweight edge-compatible client like Prisma's edge client or Upstash.

❓ What is the difference between middleware and a layout auth check?

Middleware redirects happen at the edge BEFORE the page loads — faster and more secure. Layout auth checks happen on the server after the route is matched. Use both: middleware for fast redirects, layout for fetching the full session and rendering user-specific UI.

❓ Does middleware run on API routes?

Yes — if your matcher includes /api/* paths. This is useful for adding auth headers or rate limiting to all API routes. If you do not want middleware on API routes, exclude them in the matcher config.

🚨 Common Production Issues

Missing matcher config — middleware running on every static file request (images, fonts, CSS) adds latency and costs. Always add a matcher that excludes _next/static, _next/image, and static assets.

Using Node.js APIs in middleware — the Edge Runtime does not support fs, crypto (Node.js version), or most npm packages. Use Web Crypto API, edge-compatible JWT libraries, and Upstash for Redis.

Middleware latency on every request — even 10ms of middleware latency adds up on high-traffic sites. Keep middleware logic minimal — JWT verification only, no complex business logic.

✅ Best Practices

Always add a matcher config — never run middleware on static assets

Keep middleware fast — only JWT verification and simple redirects, no complex logic

Use edge-compatible libraries — jose for JWT, Upstash for Redis

Add user ID to request headers in middleware — makes it available in all Server Components

Test middleware with next build — Edge Runtime restrictions are only enforced in production builds

🏛️ Architect's Note

Senior Architect's Perspective

Middleware is the security perimeter of your Next.js application. It is the first line of defence — fast, globally distributed, and impossible to bypass. Design your middleware to be a thin, fast gate: verify the token, set headers, redirect or pass through. All complex logic belongs in Server Components or Route Handlers.

💬 Frequently Asked Questions

How do I read request headers set by middleware in a Server Component?

Import headers() from next/headers: const headersList = await headers(); const userId = headersList.get("x-user-id"). This is the standard pattern for passing middleware-enriched data (user ID, locale, feature flags) to Server Components without re-verifying the token.

Can I have multiple middleware files?

No — there is exactly one middleware.ts file per Next.js project. Use conditional logic inside the single middleware function to handle different paths differently. For complex cases, extract helper functions into lib/middleware/ and import them.

How do I debug middleware?

Use console.log in middleware — it logs to the server terminal in development. For production debugging, use Vercel's Edge Function logs or add a custom logging header to responses. The Edge Runtime does not support Node.js debuggers.

🎯 Key Takeaways

1Middleware runs at the CDN edge before every matched request — extremely fast
2Edge Runtime: no Node.js APIs, no filesystem, no most npm packages
3Always add a matcher config to exclude static assets
4Use for auth redirects, A/B testing, geolocation, and header injection
5Keep middleware minimal — JWT verify + redirect only, no business logic
6Pass user data via request headers for Server Components to consume

🎯 Practice this topic in interviews

↗ Where this topic fits

Sources

Sources / References

Use these Next.js references to verify the concept in official docs and continue into the most relevant SplashRide pages.

Related SplashRide pages

SplashRide Next.js learning path

Browse the full Next.js topic collection and learning order on SplashRide.

Next.js Interview Prep

Practice Next.js interview questions and scenario-based answers.

Frontend Engineer Career Path

A beginner-friendly journey through web foundations, React, production frontend engineering, and interview preparation.

Full Stack Java Developer Career Path

A guided route through frontend foundations, Java backend engineering, delivery workflows, and interview readiness.

Authentication

Implement production-grade authentication in Next.js — NextAuth v5, JWT strategies, session management, protected routes, and the multi-layer auth architecture used in enterprise apps.

Route Handlers

Master Route Handlers in Next.js — the App Router replacement for API routes, supporting all HTTP methods, streaming responses, and edge runtime.