How to Use Middleware in Nextjs

How to Use Middleware in Next.js: A Comprehensive Tutorial Introduction Middleware is a powerful feature in Next.js that allows developers to run code before a request is completed. It provides a flexible way to handle tasks such as authentication, logging, redirects, and more at the edge, improving performance and user experience. Understanding how to use middleware in Next.js is essential for bu

Nov 17, 2025 - 11:16
Nov 17, 2025 - 11:16
 0

How to Use Middleware in Next.js: A Comprehensive Tutorial

Introduction

Middleware is a powerful feature in Next.js that allows developers to run code before a request is completed. It provides a flexible way to handle tasks such as authentication, logging, redirects, and more at the edge, improving performance and user experience. Understanding how to use middleware in Next.js is essential for building scalable, secure, and efficient web applications.

This tutorial covers everything you need to know about using middleware in Next.js, from the basics to advanced implementation, best practices, and real-world examples. Whether you are new to Next.js or looking to deepen your knowledge, this guide will equip you with practical skills to enhance your projects.

Step-by-Step Guide

1. Understanding Middleware in Next.js

Middleware in Next.js runs before a request is processed and can modify the response or request. Unlike traditional middleware in Node.js servers, Next.js middleware runs at the edge, closer to the user, which reduces latency.

Middleware files are placed inside the middleware.js or middleware.ts file at the root of your Next.js project or inside the app directory for the new app router.

2. Setting Up a Basic Middleware

To create middleware, add a middleware.js file in your project root. Here's a simple example that logs each request:

import { NextResponse } from 'next/server';

export function middleware(request) {

console.log('Request URL:', request.url);

return NextResponse.next();

}

This middleware intercepts every request, logs the URL, and forwards the request to the next handler.

3. Using Middleware for Redirects

Middleware can perform conditional redirects based on request data. For example, redirecting users from HTTP to HTTPS:

import { NextResponse } from 'next/server';

export function middleware(request) {

const url = request.nextUrl.clone();

if (url.protocol === 'http:') {

url.protocol = 'https:';

return NextResponse.redirect(url);

}

return NextResponse.next();

}

This ensures all traffic uses a secure HTTPS connection.

4. Protecting Routes with Middleware

You can use middleware to protect pages or APIs by checking authentication tokens or cookies. Here’s how to restrict access to a dashboard page:

import { NextResponse } from 'next/server';

export function middleware(request) {

const token = request.cookies.get('token');

const { pathname } = request.nextUrl;

if (pathname.startsWith('/dashboard') && !token) {

return NextResponse.redirect(new URL('/login', request.url));

}

return NextResponse.next();

}

This middleware checks for a token cookie; if missing, it redirects users to the login page.

5. Matching Middleware to Specific Paths

By default, middleware runs on all routes. Use the matcher configuration to target specific paths:

export const config = {

matcher: ['/dashboard/:path*', '/profile/:path*'],

};

This ensures middleware executes only on dashboard and profile routes.

6. Handling Response Rewrites

Middleware can rewrite URLs internally without redirecting the user. This is useful for internationalization or A/B testing:

import { NextResponse } from 'next/server';

export function middleware(request) {

const url = request.nextUrl.clone();

if (url.pathname === '/') {

url.pathname = '/home';

return NextResponse.rewrite(url);

}

return NextResponse.next();

}

Here, requests to the root path are rewritten to serve the /home page.

7. Combining Middleware with Edge API Routes

Since middleware runs at the edge, it complements Next.js edge API routes by handling pre-processing tasks efficiently. You can use middleware to validate requests before they reach your API endpoints.

Best Practices

1. Keep Middleware Lightweight

Middleware runs on every matched request and impacts performance. Ensure your middleware executes minimal, optimized logic to avoid slowing down responses.

2. Use Middleware for Cross-Cutting Concerns

Focus middleware on tasks like authentication, logging, redirects, or header manipulation that apply across multiple routes rather than page-specific logic.

3. Leverage the Matcher Configuration

Always specify the matcher property to limit middleware execution to relevant paths, reducing unnecessary processing.

4. Handle Edge Cases Gracefully

Consider fallback scenarios, such as missing cookies or headers, to prevent broken user experiences or infinite redirects.

5. Avoid Heavy Computation

Offload intensive computations to backend APIs or server functions instead of middleware to maintain responsiveness at the edge.

6. Secure Sensitive Data

Do not expose sensitive information in middleware logs or responses. Use environment variables and secure cookie handling.

7. Test Middleware Thoroughly

Test middleware behavior in development and staging environments to ensure it handles all expected scenarios correctly.

Tools and Resources

1. Next.js Official Documentation

The Next.js Middleware Documentation provides up-to-date information, examples, and configuration details.

2. Vercel Edge Functions

Learn about Vercel Edge Functions, which power Next.js middleware for fast, global execution.

3. Middleware Examples Repository

Explore open-source repositories with real middleware examples to understand practical use cases and implementations.

4. JavaScript and TypeScript Debugging Tools

Use debuggers like VS Code Debugger and browser devtools to step through middleware code during development.

5. Cookie and JWT Libraries

Utilize libraries such as cookie or jsonwebtoken to handle authentication tokens securely within middleware.

Real Examples

Example 1: Authentication Middleware

This middleware checks for a JWT token and redirects unauthorized users to the login page.

import { NextResponse } from 'next/server';

import jwt from 'jsonwebtoken';

export function middleware(request) {

const token = request.cookies.get('authToken');

if (!token) {

return NextResponse.redirect(new URL('/login', request.url));

}

try {

jwt.verify(token, process.env.JWT_SECRET);

return NextResponse.next();

} catch (error) {

return NextResponse.redirect(new URL('/login', request.url));

}

}

export const config = {

matcher: ['/protected/:path*'],

};

Example 2: Locale Detection and Redirect

This middleware detects the user's preferred language from headers and redirects to the corresponding localized version of the site.

import { NextResponse } from 'next/server';

export function middleware(request) {

const acceptLang = request.headers.get('Accept-Language');

const url = request.nextUrl.clone();

if (!url.pathname.startsWith('/en') && !url.pathname.startsWith('/fr')) {

if (acceptLang?.startsWith('fr')) {

url.pathname = '/fr' + url.pathname;

} else {

url.pathname = '/en' + url.pathname;

}

return NextResponse.redirect(url);

}

return NextResponse.next();

}

export const config = {

matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],

};

Example 3: Rate Limiting Middleware

A basic rate limiting example using in-memory storage to limit requests per IP address.

import { NextResponse } from 'next/server';

const rateLimitMap = new Map();

export function middleware(request) {

const ip = request.ip ?? 'unknown';

const now = Date.now();

const windowTime = 60000; // 1 minute

const maxRequests = 10;

if (!rateLimitMap.has(ip)) {

rateLimitMap.set(ip, []);

}

const timestamps = rateLimitMap.get(ip).filter(timestamp => now - timestamp < windowTime);

if (timestamps.length >= maxRequests) {

return new NextResponse('Too many requests', { status: 429 });

}

timestamps.push(now);

rateLimitMap.set(ip, timestamps);

return NextResponse.next();

}

export const config = {

matcher: ['/api/:path*'],

};

FAQs

What is the difference between middleware and API routes in Next.js?

Middleware runs before a request is processed and can modify or redirect requests globally or on matched routes. API routes are backend endpoints that handle specific requests and return data. Middleware is ideal for cross-cutting concerns like authentication or redirects, whereas API routes handle business logic and data processing.

Can middleware access request body data?

No, Next.js middleware currently does not support accessing the request body because it runs at the edge and is designed for quick, lightweight operations on request metadata like headers, cookies, and URLs.

How do I debug middleware in Next.js?

You can add console logs and use remote debugging tools provided by your development environment. Since middleware runs at the edge, logs appear in your terminal or cloud provider logs. Use tools like VS Code debugger for local testing.

Does middleware affect SEO?

Middleware can positively impact SEO by handling redirects, locale detection, and security headers efficiently. Proper use of middleware ensures search engines receive correct URLs and content, enhancing SEO performance.

Can I use middleware with static site generation (SSG)?

Yes, middleware works with SSG by running at request time before serving static pages. This enables dynamic behavior like authentication or localization even on statically generated sites.

Conclusion

Middleware in Next.js is a versatile and essential feature for enhancing your web applications with pre-request processing capabilities. From authentication and redirects to localization and rate limiting, middleware empowers you to manage cross-cutting concerns efficiently at the edge.

By following this tutorial’s step-by-step guide, best practices, and examples, you can implement middleware in your projects confidently and optimize your Next.js applications for performance, security, and user experience. Continue exploring the available tools and resources to stay updated with the latest Next.js features and middleware patterns.