Next.js 16 Middleware to proxy.ts: Why It's Not Working (Tested)
In Next.js 16, middleware.ts became proxy.ts and the exported middleware function became proxy. The old file still works for now, which is why a half-finished migration is the usual cause of "my middleware isn't running".
I tested every combination on Next.js 16.4.4. The proxy set a response header, and I checked with curl whether it was there after next build and next start.
What runs and what doesn't
setup build proxy runs?
middleware.ts, export function middleware passes yes, with a deprecation warning
proxy.ts, export function proxy passes yes
proxy.ts, export default function passes yes
proxy.ts, export function middleware fails -
middleware.ts and proxy.ts both present fails -
proxy.ts inside app/ passes no, silently
proxy.ts at the root, but the app is in src/ passes no, silently
src/proxy.ts with src/app passes yes
proxy.ts with runtime: "edge" fails -The two silent rows are the ones that cost time: the build passed, the build output didn't list a proxy, and no request was touched. When it works, next build lists "ƒ Proxy (Middleware)" at the bottom of the route table. If that line is missing, Next.js didn't find your file.
The old file still works, with a warning
⚠ The "middleware" file convention is deprecated. Please use "proxy" instead.
npx @next/codemod@canary middleware-to-proxy .
Learn more: https://nextjs.org/docs/messages/middleware-to-proxymiddleware.ts exporting middleware still ran on every request. You don't have to migrate the day you upgrade, but the warning means it will go away.
Errors from mixing the names
Renaming the file but not the function failed the build:
Error: Proxy is missing expected function export name
This function is what Next.js runs for every request handled by this proxy (previously called middleware).
- You are migrating from `middleware` to `proxy`, but haven't updated the exported function.Export it as proxy, or as a default export: both ran. Leaving the old file next to the new one also failed: "Both middleware file "./middleware.ts" and proxy file "./proxy.ts" are detected. Please use "./proxy.ts" only."
Where the file has to be
proxy.ts goes in the project root, next to app/ (or pages/). If your app lives in src/app, it goes in src/, next to that app folder. I tried proxy.ts inside app/ and at the root of a src/ project: both builds passed and the proxy never ran.
my-app/ my-app/
app/ src/
proxy.ts ✓ app/
proxy.ts ✓Edge runtime is gone for proxy
Error: Next.js can't recognize the exported `config` field in route. Proxy does not support Edge runtime.proxy.ts runs on the Node.js runtime; setting runtime: "edge" failed the build. If your middleware relied on edge-only behaviour, or you deploy somewhere that runs middleware at the edge, test it after the move. Setting runtime: "edge" on the old middleware.ts also failed on 16.4.4, with "the edge runtime for rendering is currently experimental. Use runtime 'experimental-edge' instead."
It runs on every request unless you add a matcher
Without a matcher, my proxy ran on the page, on /robots.txt and on the JavaScript chunks under /_next/static. Auth checks or redirects there can break the page's own assets. Limit it to the paths you need:
// proxy.ts
import { NextResponse, type NextRequest } from "next/server";
export function proxy(request: NextRequest) {
// ...
return NextResponse.next();
}
export const config = {
matcher: "/admin/:path*",
};With that matcher the header appeared on /admin and not on other pages. To run everywhere except assets, the docs use a negative lookahead:
export const config = {
matcher: ["/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)"],
};The codemod
npx @next/codemod@latest middleware-to-proxy .I ran version 16.4.5 on a middleware.ts that had export const config = { runtime: "edge" }. It renamed the file to proxy.ts and the function to proxy, and it removed the config export entirely. With only a runtime in it that's what you want, but check the diff if your config also had a matcher next to the runtime.
Checklist
- File: proxy.ts in the root, or in src/ if you use src/app. Not inside app/.
- Export: function proxy, or a default export. Not middleware.
- Delete middleware.ts once proxy.ts exists.
- Look for "ƒ Proxy (Middleware)" in the next build output.
- Add a matcher so it skips /_next/static and other assets.
- Remove runtime: "edge"; proxy runs on Node.js.
Related
- params should be awaited: tested on Next.js 16
- Next.js redirects: 307 vs 308 for SEO
- Next.js trailing slash
- Next.js development