javascriptreactjsnext.js

Why is Next.js middleware running multiple times?


I created a fresh Next.js using npx create-next-app@latest --typescript. After installation (the version being 13.3.4), without changing any files, I added a new middleware.ts file inside the src folder, and I only put this block of code:

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

export function middleware(request: NextRequest) {
  console.log("request", JSON.stringify(request));
  return NextResponse.next();
}

The console log is hit multiple times. I think it should be once, right? Is there any configuration I need to do for this fresh Next.js installation?

Note: I am going to do some cookie logic here in the middleware for authentication. Screenshot:

enter image description here


Solution

  • This is normal because a middleware by default runs for every request, including the ones for getting assets like JavaScritp, CSS, and image files. As you can read on the doc:

    Middleware will be invoked for every route in your project. The following is the execution order:

    1. headers from next.config.js
    2. redirects from next.config.js
    3. Middleware (rewrites, redirects, etc.)
    4. beforeFiles (rewrites) from next.config.js
    5. Filesystem routes (public/, _next/static/, pages, etc.)
    6. afterFiles (rewrites) from next.config.js
    7. Dynamic Routes (/blog/[slug])
    8. fallback (rewrites) from next.config.js

    If you add a console.log(request.nextUrl.pathname), you would see the different paths for which it's running. To have it execute for some paths only, you need to use conditional statements or the matcher config, like so:

    // middleware.ts
    
    import type { NextRequest } from "next/server";
    import { NextResponse } from "next/server";
    
    export function middleware(request: NextRequest) {
      console.log("Request for : ", request.nextUrl.pathname);
      return NextResponse.next();
    }
    
    export const config = {
      // The above middleware would only run for the "/" path
      matcher: '/',
    }
    

    Another matching pattern that's used a lot is this one:

    // middleware.ts
    
    import type { NextRequest } from "next/server";
    import { NextResponse } from "next/server";
    
    export function middleware(request: NextRequest) {
      console.log("Request for : ", request.nextUrl.pathname);
      return NextResponse.next();
    }
    
    export const config = {
      matcher: [
        /*
         * 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)
         */
        '/((?!api|_next/static|_next/image|favicon.ico).*)',
      ],
    }