SyntaxStudy
Sign Up
Next.js Middleware for Auth, Redirects, and Edge Logic
Next.js Beginner 1 min read

Middleware for Auth, Redirects, and Edge Logic

Next.js Middleware runs before a request is completed and can rewrite, redirect, modify headers, or return a response directly. It is defined in a middleware.ts file at the root of the project (alongside app/ or src/). Middleware runs on the Edge Runtime by default, meaning it runs close to the user on a CDN edge network with extremely low latency, but with a restricted API (no Node.js built-ins). The config export in middleware.ts controls which routes the middleware runs on via the matcher option. You can specify exact paths, glob patterns, or negative lookaheads. A common pattern is to run middleware on all routes except static files and Next.js internals. Middleware is the recommended place to enforce authentication — check the session cookie and redirect to /login before the page handler runs, rather than checking in each page component. Request headers and cookies can be read and written in Middleware via the request.headers and request.cookies APIs on the NextRequest. You can also set response headers by creating a NextResponse and calling response.headers.set(). A useful pattern is to forward the current URL as a request header so server components downstream can read it without needing the request object directly.
Example
// middleware.ts (at project root)

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { verifyJwt } from '@/lib/jwt'; // must be edge-compatible

export async function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;

  // Protected routes
  const isProtected = pathname.startsWith('/dashboard') ||
    pathname.startsWith('/settings');

  if (isProtected) {
    const token = request.cookies.get('session')?.value;

    if (!token) {
      // Redirect to login with a return URL
      const loginUrl = new URL('/login', request.url);
      loginUrl.searchParams.set('callbackUrl', pathname);
      return NextResponse.redirect(loginUrl);
    }

    try {
      await verifyJwt(token);
    } catch {
      const loginUrl = new URL('/login', request.url);
      return NextResponse.redirect(loginUrl);
    }
  }

  // Forward the pathname to server components via a header
  const requestHeaders = new Headers(request.headers);
  requestHeaders.set('x-pathname', pathname);

  return NextResponse.next({ request: { headers: requestHeaders } });
}

export const config = {
  matcher: [
    // Skip Next.js internals and all static files
    '/((?!_next/static|_next/image|favicon.ico).*)',
  ],
};