How to Create Custom Middleware in Next.js

Introduction

Middleware in Next.js allows you to run code before a request is completed, which can be useful for tasks like authentication, logging, or redirects. Next.js introduced middleware support starting from version 12, enabling you to create and use custom middleware for your application.

Understanding Next.js Middleware

Middleware functions run before a request is completed, allowing you to.

  • Modify request and response objects
  • Redirect requests
  • Implement authentication and authorization
  • Log information

Setting Up Middleware
 

Create a Middleware File

Create a _middleware.js file inside the pages directory or any specific subdirectory where you want the middleware to apply. Middleware in Next.js can be applied globally or to specific routes.

Example. Global Middleware.

// pages/_middleware.js
import { NextResponse } from 'next/server';
export function middleware(request) {
  const { pathname } = request.nextUrl; 
  // Redirect example
  if (pathname === '/old-page') {
    return NextResponse.redirect(new URL('/new-page', request.url));
  }  
  // Add custom logic here
  return NextResponse.next();
}

Example. Route-Specific Middleware.

Place the middleware file in the specific directory you want it to apply to.

// pages/api/_middleware.js
import { NextResponse } from 'next/server';
export function middleware(request) {
  // Middleware logic for API routes
  return NextResponse.next();
}

Use Middleware for Authentication

Create middleware to protect certain pages by checking user authentication status.

// pages/_middleware.js
import { NextResponse } from 'next/server';
import { getToken } from 'next-auth/jwt';
export async function middleware(request) {
  const token = await getToken({ req: request });
  if (!token && request.nextUrl.pathname !== '/login') {
    return NextResponse.redirect(new URL('/login', request.url));
  }
  return NextResponse.next();
}

Testing Middleware
 

Run your Development Server

Ensure middleware logic is correctly applied by running your development server.

npm run dev

Test Functionality

Test different routes and scenarios to verify that middleware behaves as expected.

  • Check redirects
  • Verify authentication checks
  • Ensure middleware does not unintentionally block or modify requests

Advanced Middleware Techniques
 

Dynamic Middleware

You can create dynamic middleware to handle different environments or conditions.

// pages/_middleware.js
import { NextResponse } from 'next/server';
export function middleware(request) {
  const isDev = process.env.NODE_ENV === 'development';
  if (isDev) {
    console.log('Development mode');
  }
  return NextResponse.next();
}

Custom Response Headers

Add custom headers to responses.

// pages/_middleware.js
import { NextResponse } from 'next/server';

export function middleware(request) {
  const response = NextResponse.next();
  response.headers.set('X-Custom-Header', 'MyHeaderValue');
  return response;
}

Best Practices

  • Keep It Simple: Avoid overly complex logic in middleware to maintain readability and performance.
  • Limit Scope: Apply middleware only to necessary routes to avoid unintended side effects.
  • Secure Middleware: Ensure middleware that handles authentication or sensitive data is secure and free from vulnerabilities.

Summary

Custom middleware in Next.js provides a powerful way to intercept and modify requests. By following this guide, you can create middleware to handle various tasks such as authentication, redirection, and response customization. Test your middleware thoroughly and adhere to best practices for a secure and efficient implementation.