Study notes · 10% of the exam

Proxy (Middleware), Redirects and Rewrites

Next.js 16 renamed Middleware to Proxy: one `proxy.ts` file that runs on Node.js before routes render and can redirect, rewrite, set headers and cookies, or respond directly. Know where it sits in the request order, how matchers work, and why it is only an optimistic layer for auth.

Key points

  1. 1

    Put proxy.ts at the same level as app (the root, or src/ if you use it) and export one function named proxy or a default export. Only one proxy file is allowed per project. middleware.ts is deprecated; the middleware-to-proxy codemod renames it.

  2. 2

    Proxy defaults to the Node.js runtime and the runtime option can't be set in it (doing so throws). Teams that still need Edge are told to keep using middleware for now.

  3. 3

    Order of checks: next.config headers → redirects → Proxy → beforeFiles rewrites → filesystem routes (public/, _next/static, pages) → afterFiles rewrites → dynamic routes → fallback rewrites. Config redirects therefore win over Proxy.

  4. 4

    Without a matcher, Proxy runs on every request, including _next/static, _next/image and public/ files. Matchers must be static constants, are anchored to the start of the path, and use path-to-regexp modifiers: * zero or more, + one or more, ? zero or one. Objects can add has, missing and locale.

  5. 5

    redirect (default 307, URL must be absolute) changes the browser URL. rewrite serves another route or an external URL while keeping the URL. next({ request: { headers } }) forwards headers upstream, while next({ headers }) sends them to the client.

  6. 6

    Server Functions are POSTs to the page they're used on, so the matcher decides whether Proxy sees them. Proxy still runs for _next/data even if excluded. Flight headers such as next-router-prefetch are stripped from request.headers, so skip prefetches with matcher missing conditions.

  7. 7

    Keep Proxy fast: it runs on prefetches too. fetch cache options have no effect, module-level state isn't shared between instances, and event.waitUntil() lets background work finish after the response.

Common traps

  • Treating Proxy as your authorization layer. A matcher change, a moved Server Function or a framework bug (CVE-2025-29927) can skip it, so always re-check in the Data Access Layer and in every Server Function.

  • Negative matchers that are too broad or too narrow: (?!api also excludes /apiary, and forgetting to exclude public/ assets makes auth or locale redirects break logos, favicons and the login page itself.

  • Leaving proxy.ts at the repository root after moving routes into src/app. Proxy silently stops running, with no error.

Test yourself on Proxy (Middleware), Redirects and Rewrites

Ten questions, with the answer and explanation after each one.