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
Put
proxy.tsat the same level asapp(the root, orsrc/if you use it) and export one function namedproxyor a default export. Only one proxy file is allowed per project.middleware.tsis deprecated; themiddleware-to-proxycodemod renames it. - 2
Proxy defaults to the Node.js runtime and the
runtimeoption can't be set in it (doing so throws). Teams that still need Edge are told to keep usingmiddlewarefor now. - 3
Order of checks:
next.configheaders → redirects → Proxy →beforeFilesrewrites → filesystem routes (public/,_next/static, pages) →afterFilesrewrites → dynamic routes →fallbackrewrites. Config redirects therefore win over Proxy. - 4
Without a matcher, Proxy runs on every request, including
_next/static,_next/imageandpublic/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 addhas,missingandlocale. - 5
redirect(default 307, URL must be absolute) changes the browser URL.rewriteserves another route or an external URL while keeping the URL.next({ request: { headers } })forwards headers upstream, whilenext({ headers })sends them to the client. - 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/dataeven if excluded. Flight headers such asnext-router-prefetchare stripped fromrequest.headers, so skip prefetches with matchermissingconditions. - 7
Keep Proxy fast: it runs on prefetches too.
fetchcache options have no effect, module-level state isn't shared between instances, andevent.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:
(?!apialso excludes/apiary, and forgetting to excludepublic/assets makes auth or locale redirects break logos, favicons and the login page itself.Leaving
proxy.tsat the repository root after moving routes intosrc/app. Proxy silently stops running, with no error.
Read the source
Test yourself on Proxy (Middleware), Redirects and Rewrites
Ten questions, with the answer and explanation after each one.