What is Middleware?

In Next.js, Middleware is a piece of code that runs before a request is handled by a page or an API route. It acts as a gatekeeper, allowing you to intercept the incoming request and modify it or its response.

The most powerful feature of Middleware is that it runs on the Edge.

What is the Edge?

Instead of running on a traditional server in one specific location (e.g., a "us-east-1" region), Edge Functions are deployed to a global network of servers (a CDN). When a user makes a request, the Middleware code is executed in the data center physically closest to them.

This results in extremely low latency, making it perfect for tasks that need to be fast for every user, no matter where they are.

How to Create Middleware

  1. Create a file named middleware.js (or .ts) in the root of your project (or inside the src folder, if you use one).
  2. Export a function named middleware from this file.

This single file will handle middleware for your entire application.

A Basic Middleware (middleware.ts):

TypeScript


import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

// This function can be marked `async` if you need to use `await`
export function middleware(request: NextRequest) {
  // You can inspect the request here...
  console.log('Middleware running for path:', request.nextUrl.pathname);

  // You can return a response to modify the request flow
  // For now, let's just let the request pass through
  return NextResponse.next();
}

The matcher: Controlling Where Middleware Runs

By default, Middleware runs on every single request to your application, which can be inefficient. The matcher config allows you to specify exactly which paths the Middleware should execute for.

Example matcher config in middleware.ts:

TypeScript


// Add this config object to your middleware.ts file
export const config = {
  // Match all request paths except for the ones starting with:
  // - api (API routes)
  // - _next/static (static files)
  // - _next/image (image optimization files)
  // - favicon.ico (favicon file)
  matcher: [
    '/((?!api|_next/static|_next/image|favicon.ico).*)',
  ],
}

This is a common configuration to avoid running middleware on static assets and API calls where it's not needed.

Common Use Cases

Middleware is incredibly versatile. Here are some common patterns:

  1. Authentication: Check if a user has a session cookie. If not, redirect them to the /login page.
  2. TypeScript

export function middleware(request: NextRequest) {
  const sessionCookie = request.cookies.get('session');
  const { pathname } = request.nextUrl;

  // Redirect to login if no session and not already on the login page
  if (!sessionCookie && pathname !== '/login') {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  return NextResponse.next();
}

export const config = {
  matcher: ['/dashboard/:path*', '/settings'],
};
  1. A/B Testing: Based on a cookie or other logic, you can "rewrite" the URL to show a user a different version of a page without changing the URL in their browser.
  2. TypeScript

export function middleware(request: NextRequest) {
  // 50% chance to be in the new page group
  if (Math.random() < 0.5) {
    // Rewrite to the new page, but the user still sees '/about' in the URL
    return NextResponse.rewrite(new URL('/about/new', request.url));
  }
  return NextResponse.next();
}
  1. Localization: Detect a user's country from the request headers and redirect them to a localized version of your site (e.g., /en-US or /fr-FR).