Matcher

Choosing Paths

config.matcher limits which paths run the middleware. Without it, every request (including /_next/static files) runs it.

export const config = {
  matcher: [
    '/account/:path*',
    '/admin/:path*',
    '/checkout',
    // everything except API routes, static files and images:
    // '/((?!api|_next/static|_next/image|favicon.ico).*)',
  ],
};

How Path Patterns Match

Example

Convert matcher patterns like /admin/:path* into regular expressions:

function toRegex(pattern: string): RegExp {
  const src = pattern
    .replace(/\/:\w+\*/g, '(?:/.*)?') // /:path*  -> zero or more segments
    .replace(/\/:\w+\+/g, '/.+')      // /:path+  -> one or more segments
    .replace(/\/:\w+/g, '/[^/]+');     // /:id     -> exactly one segment
  return new RegExp('^' + src + '$');
}

const matchers = ['/account/:path*', '/admin/:path+', '/orders/:id'];
const paths = ['/account', '/account/orders', '/admin', '/admin/products', '/orders/9', '/orders/9/invoice', '/products'];

for (const path of paths) {
  const hit = matchers.find((m) => toRegex(m).test(path));
  console.log(path.padEnd(18), hit ?? '(middleware skipped)');
}

Output

/account           /account/:path*
/account/orders    /account/:path*
/admin             (middleware skipped)
/admin/products    /admin/:path+
/orders/9          /orders/:id
/orders/9/invoice  (middleware skipped)
/products          (middleware skipped)