Middleware

The file src/app/middleware.ts runs once per request BEFORE page dispatch. Returning undefined passes the request through; returning a Response short-circuits with that response (status, headers, body).

Tip
Always set config.matcher to target only the relevant paths — without it every request (assets, API calls, pages) pays the V8 round-trip, wiping out ISR cache-hit performance for all static routes.

Try it

src/app/middleware.tsTypeScript
// src/app/middleware.ts
//
// config.matcher is a path filter. Without it every request — assets,
// API hits, page loads — pays the V8 round-trip just to get a pass-
// through. Listing only the gated paths lets the rest of the site
// serve at the cache-hit ceiling.
export const config = {
  matcher: ["/examples/middleware/protected/:path*"],
};

export default function middleware(
  request: Request,
  ctx: { path: string },
): Response | undefined {
  if (ctx.path.startsWith("/examples/middleware/protected")) {
    const url = new URL(request.url);
    if (url.searchParams.get("token") !== "letmein") {
      return new Response("Unauthorized.\n", {
        status: 401,
        headers: { "content-type": "text/plain" },
      });
    }
  }
  return undefined; // pass-through
}