Technology · Next.js

Next.js Middleware

Use Next.js middleware for redirects, auth checks, headers, and request rewriting in the App Router.

TL;DR
  1. 01Create a middleware.ts file at the project root to intercept requests.
  2. 02Use middleware to check auth, redirect, or modify headers.
  3. 03Return NextRequest or NextResponse to control the request flow.

Basic Setup

  • Create a middleware.ts file 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 matcher export 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 matcher config 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.

Next.js InternationalizationNext.js Server Components