Middleware Matcher & Config
middleware.ts runs on every single request to your app — every page, every image, every font, every API call. That's rarely what you want. The exported config.matcher lets you restrict middleware to only the paths that actually need it./favicon.ico or files under /_next/static — pays the cost of running your middleware function first. On a busy site this adds latency to traffic that middleware was never meant to touch. Always scope middleware with a matcher unless you genuinely need it on every path.The matcher config
config object alongside your middleware function with a matcher field — either a single path pattern or an array of them.middleware.ts
import { NextRequest, NextResponse } from 'next/server'
export function middleware(request: NextRequest) {
// ...auth check...
return NextResponse.next()
}
export const config = {
matcher: ['/dashboard/:path*', '/settings/:path*'],
}/dashboard and /settings and everything nested under them, leaving the marketing homepage, blog, and static assets completely untouched by middleware.Matcher syntax
Pattern | Matches |
|---|---|
| Exactly |
|
|
| Exactly one segment after |
| A regex-style negative-lookahead pattern — everything except the listed prefixes |
The last pattern above is the common "run on every page but skip infrastructure paths" matcher, and is close to what the Next.js default template ships with when middleware needs broad coverage minus assets and API routes.
middleware.ts — excluding assets and API routes
export const config = {
matcher: [
'/((?!api|_next/static|_next/image|favicon.ico).*)',
],
}Filtering inside the function instead
request.nextUrl.pathname inside the middleware function itself and return early with NextResponse.next(). Combining a broad matcher with an early return inside the function is a normal pattern.middleware.ts — conditional early return
export function middleware(request: NextRequest) {
if (request.nextUrl.pathname.startsWith('/api/public')) {
return NextResponse.next()
}
// ...auth check for everything else the matcher covers...
}/dashboard/*" than to remember every early-return condition buried inside a function that technically runs on every request.config export — not a value computed at runtime.Without a
matcher, middleware runs on every request, including static assets — a real performance cost at scale.config.matcheraccepts one path or an array of path patterns (:path*for nested segments,:slugfor a single segment, or a regex-style pattern).A common broad pattern excludes
api,_next/static,_next/image, andfavicon.icowhile covering everything else.You can combine a matcher with an early
NextResponse.next()return inside the function for logic too dynamic for a static pattern.Scope middleware as narrowly as the use case allows.