Developer Tools

Next.js Middleware Matcher Tester

Paste the matcher from middleware.ts or proxy.ts, list your routes, and see instantly which ones run your middleware and which are excluded. The tester uses the same pattern code as Next.js 15.5 and 16, including the hidden _next/data and .rsc variants, basePath, i18n and has / missing conditions, and explains why a route was skipped.

Next.js Middleware Matcher Tester

Free · No sign-up
Loading tool…

Test your Next.js middleware matcher before you deploy

A matcher decides which requests reach your middleware, and a small mistake either leaves a page unprotected or sends your CSS, images and API calls through an auth redirect. This Next.js middleware matcher tester shows the answer for every route at once: green when the middleware runs, grey when the route is skipped.

It doesn't guess with a generic regex tester. The matcher is compiled the way next build compiles it, with path-to-regexp 6.3.0 and the prefixes and suffixes Next.js adds, and the result was checked against Next.js's own matching function on thousands of route and pattern combinations.

How to exclude paths from Next.js middleware

The usual way to exclude paths is a negative lookahead: '/((?!api|_next/static|_next/image|favicon.ico).*)' runs the middleware on every path except the ones that start with those words. Paste it above and add your own routes to see exactly what it leaves out.

One catch surprises many developers: the lookahead checks how the path starts, not whole folder names. 'api' also excludes /apiary and /api-docs. Write 'api/' or 'api(?:/|$)' when you only mean the API folder; the tester points this out when it happens.

Why your Next.js middleware matcher is not working

Most matcher bugs come from a handful of causes. The config must be exported as export const config with literal strings, because Next.js reads it at build time; variables and template strings with ${…} are ignored. Every pattern must start with a slash, and matching is case-sensitive.

Named parameters only cover one segment: '/dashboard/:path' matches /dashboard/settings but not /dashboard/settings/billing, while '/dashboard/:path*' covers both and /dashboard itself. A plain '/about' matches /about but not /about/team. In a JavaScript string, a single backslash before a dot is dropped, so write '\\.' to match a literal dot. The tester flags each of these.

Match all routes, or only some

Leave the matcher out and middleware runs on every request, static files included. For all pages but no static assets, use a negative lookahead; for a few protected sections, list them in an array such as ['/dashboard/:path*', '/account/:path*']. Multiple matchers are checked in order, and the tester shows which one matched each route.

Ignore the public folder and static files

Files in the public folder are served from the site root, so /logo.png and /robots.txt pass through middleware unless the matcher excludes them. A common fix is to skip any path that contains a dot, as in '/((?!_next|.*\\..*).*)'. Test it with your real routes first: a page such as /blog/version-1.2 also contains a dot and would be skipped too.

Hidden routes: _next/data, .rsc and prefetches

Next.js quietly extends every matcher. Pages Router data requests (/_next/data/<build-id>/page.json) and App Router .rsc and segment prefetch requests match whenever the page matches, even if your pattern excludes _next/data. That keeps data routes protected along with their pages.

To skip prefetches, use the object form with missing: [{ type: 'header', key: 'next-router-prefetch' }]. Add the headers and cookies of a request in the options above to test has and missing conditions.

Fewer middleware calls: faster pages and lower costs

Every request that matches runs your middleware before Next.js can send a response. On a typical page that includes JavaScript chunks, images, fonts and the favicon, so a matcher that lets them through adds work to dozens of requests, and on hosts that bill per middleware or function invocation, it adds to the bill as well.

Checking the path inside the middleware with if statements doesn't avoid this, because the function still starts for every request. Excluding static files in the matcher means those requests never reach it at all. Use the tester to confirm that _next/static, images and other public files are skipped while every page that needs protection still runs.

Next.js 16: proxy.ts uses the same matcher

Next.js 16 renamed middleware.ts to proxy.ts and the exported function to proxy, and it runs on the Node.js runtime by default. The config and matcher syntax didn't change, so the Next.js proxy matcher works exactly like the middleware matcher, and this tester covers both.

basePath, i18n and the regex behind it

If your next.config sets a basePath or Pages Router i18n locales, enter them in the options and the matcher is compiled with them, just like in a build. Open the regex panel to see and copy the final regular expression Next.js uses for each pattern.

Protecting routes with a JWT? Our JWT decoder shows what's inside the token your middleware checks and when it expires. If an AI assistant edits your middleware, add the routing rules to a CLAUDE.md or Cursor rules file.

Private by design

Your config and routes never leave the page. The tester is plain code running in your browser, with no upload and no account, like all of our free developer tools that run in your browser.

Frequently asked questions

How do I exclude a path from Next.js middleware?

Use a negative lookahead such as '/((?!api|_next/static|_next/image|favicon.ico).*)' and add the paths to skip inside (?!…). Paste it above with your routes to check what is excluded.

Why is my Next.js middleware matcher not working?

The usual causes are a matcher built from variables, a pattern without a leading slash, ':path' where ':path*' is needed, a single backslash before a dot in a JavaScript string, or a lookahead word that also excludes other paths. The tester reports each one.

What does :path* mean in a Next.js matcher?

':path' is a named parameter for one path segment, and the * makes it zero or more segments. '/dashboard/:path*' matches /dashboard, /dashboard/settings and /dashboard/settings/billing. Use + for one or more and ? for zero or one.

Why does my middleware run on favicon.ico and images?

Without a matcher, middleware runs on every request, including _next/static files, image optimization and files in the public folder. Exclude them with a negative lookahead or a pattern that skips paths with a file extension.

Should I use config.matcher or if statements in middleware?

Use the matcher to decide where middleware runs at all, and if statements for decisions that depend on the request, such as cookies or roles. Requests the matcher excludes never start the middleware, which saves time and invocations.

Does '/about' match '/about/team'?

No. A plain path matches only itself (plus its .rsc and data variants). Use '/about/:path*' to match /about and every route below it.

How do I run middleware on all routes except static files?

Exclude Next.js internals and files with an extension, for example '/((?!_next|.*\\..*).*)'. Check pages with a dot in their URL, because they would be skipped as well.

Why does my middleware still run on _next/data?

Next.js always matches data routes when the matching page would match, even when _next/data is in a negative lookahead. This is intentional, so a page's data can't bypass the check that protects the page.

Does the matcher work the same with proxy.ts in Next.js 16?

Yes. Next.js 16 renamed middleware to proxy, but the matcher syntax and the way it is compiled are the same, so the results here apply to both.

How do I skip or disable middleware for one route?

Add the route to the negative lookahead, for example '/((?!api|_next/static|_next/image|favicon.ico|public-page).*)', or list only the routes that need middleware. To skip it for certain requests, use missing with a header or cookie. Test the change here before you deploy.

Can I use multiple matchers or has / missing conditions?

Yes. Paste an array of strings and objects. Objects can use source, locale: false, has and missing; add request headers and cookies in the options to test conditions.

Is my config uploaded anywhere?

No. Everything runs in your browser. Nothing is sent to a server or an AI model.