#CodeWorksLab()
HomeTutorialsAboutContact

Understanding Next.js Middleware: A Complete Guide

17 April 2025
World

Understanding Next.js Middleware: A Complete Guide


Read time: 7 min
Published: April 23, 2025
Author: CodeWorksLab

Figure 1. Middleware gives you control over every incoming request in Next.js.

Table of Contents

  1. What Is Next.js Middleware?
  2. Key Use-Cases
  3. How It Works Under the Hood
  4. Step-by-Step Example
  5. Best Practices & Tips
  6. Common Pitfalls
  7. Performance Considerations
  8. Conclusion & Next Steps

What Is Next.js Middleware?


Next.js Middleware is a special function that runs before every request is processed, on the Edge. It lives in the /middleware.js (or /middleware.ts) file at your project root and has direct access to:

  • Request URL, headers & cookies
  • Rewrite, redirect, or modify responses
  • Run at the edge, with very low latency


In a nutshell: Middleware lets you intercept and transform requests globally, without touching your application routes or API functions.

Key Use-Cases

  1. Authentication & Authorization
    • Redirect unauthenticated users to /login
    • Protect admin or dashboard routes

// middleware.js
import { NextResponse } from 'next/server';
export function middleware(req) {
const token = req.cookies.get('token');
if (req.nextUrl.pathname.startsWith('/dashboard') && !token) {
return NextResponse.redirect(new URL('/login', req.url));
}
return NextResponse.next();
}


  1. A/B Testing & Feature Flags
    • Serve different variants based on headers or cookies
    • Run experiments without touching client code
  2. Geo-Location
    • Detect x-vercel-ip-country header
    • Rewrite /us/pricing vs /eu/pricing
  3. Bot Protection & Rate-Limiting
    • Block known bad user-agents
    • Throttle requests based on IP
  4. Custom Headers & Caching
    • Inject security headers (CSP, HSTS)
    • Add Cache-Control for static assets

How It Works Under the Hood

  • Placement: a single /middleware.js at project root (or per-directory).
  • Execution: Edge Runtime, V8 isolates—no Node APIs allowed, but ultra-fast.
  • Routing: You define matchers in next.config.js to scope which paths trigger middleware:

// next.config.js
module.exports = {
middleware: {
// only run on /dashboard and /api/*
matcher: ['/dashboard/:path*', '/api/:path*'],
},
}


  • Return Values:
    • NextResponse.next() → proceed as normal
    • NextResponse.redirect(url) → send a 307/308 redirect
    • NextResponse.rewrite(dest) → internally rewrite without changing URL
    • You can also add/modify cookies and headers on the returned response object.

Step-by-Step Example: Protecting a Dashboard


Let’s build a middleware that guards /dashboard routes:

  1. Create /middleware.js at project root:

import { NextResponse } from 'next/server';

export function middleware(request) {
const { pathname } = request.nextUrl;
// 1) Only run on /dashboard
if (pathname.startsWith('/dashboard')) {
// 2) Check for an auth cookie
const token = request.cookies.get('auth-token');
if (!token) {
// 3) Redirect to /login
const loginUrl = new URL('/login', request.url);
loginUrl.searchParams.set('redirect', pathname);
return NextResponse.redirect(loginUrl);
}
}
return NextResponse.next();
}


  1. Configure matcher in next.config.js:

module.exports = {
middleware: {
matcher: ['/dashboard/:path*'],
},
};

  1. Test it out
    • Visit /dashboard without the auth-token cookie → you’ll be bounced to /login?redirect=/dashboard.
    • After login, set the auth-token cookie and you’ll be let through.

Best Practices & Tips

  • Keep Logic Minimal Heavy computations in middleware slow down every request. Offload complex logic to API routes if possible.
  • Use Matchers Scope your middleware narrowly to avoid unnecessary invocations.
  • Leverage Cookies Use request.cookies.get() to read and response.cookies.set() to write cookies at the edge.
  • Chainable Responses You can append headers easily:

const res = NextResponse.next();
res.headers.set('X-Frame-Options', 'DENY');
return res;


  • Test Locally Run next dev and watch your middleware logs for each request. Edge errors will surface in your terminal.

Common Pitfalls

  1. Infinite Redirect Loops
    • Don’t redirect /login back to itself. Always check and exclude the target path.
  2. Missing matcher
    • Without it, middleware runs on every path—including static assets—adding unnecessary overhead.
  3. Using Node-Only APIs
    • Edge Runtime does not support fs, net, etc. Stick to Web-standard APIs and @vercel/kv or fetch.
  4. Over-Rendering
    • Don’t call external APIs from middleware—this will block your response. Instead, store small lookup tables in edge-cache or KV.

Performance Considerations

  • Cold Starts at the edge are fast (~5 ms), but doing heavy work adds latency.
  • No bundler warnings but you can still tree-shake unused code.
  • Monitor your Edge Observatory in Vercel or use custom logging to measure request.time.


Pro Tip: If you only need rewrites or redirects, consider using next.config.js Redirects/Rewrites instead of middleware for zero-code edge config.

Conclusion & Next Steps


Middleware in Next.js opens up incredible possibilities—authentication, A/B tests, geo-routing, and more—all at the edge. To keep users engaged, follow these steps:

  1. Start small: protect one route or add a single header.
  2. Benchmark before and after (use Vercel Analytics & Lighthouse).
  3. Iterate: add matchers, caching, and minimal logic.
  4. Share your learnings: write a mini-tutorial on your blog or tweet your code snippet!

Enjoyed this deep dive? ⭐️ Star CodeWorksLab on GitHub and subscribe for more in-depth Next.js tutorials!

Stay Connected

© 2025 CodeWorksLab