Skip to content
  • Next.js
  • Proxy
  • Middleware
  • Security
  • Web Development

Next.js 16 proxy.ts vs middleware.ts: What Changed and How to Migrate

By Published 3 min read (or skim, I won't know)

In Next.js 16, middleware.ts is renamed to proxy.ts and its exported function from middleware to proxy. It still runs before a request reaches your routes, it now runs on the Node.js runtime, and you migrate with one codemod.

What changed from middleware to proxy?

Next.js 15Next.js 16
Filemiddleware.tsproxy.ts (project root or src/)
Exportexport function middleware()export function proxy() or a default export
RuntimeEdge by defaultNode.js (cannot be changed)
Config flagskipMiddlewareUrlNormalizeskipProxyUrlNormalize
StatusCurrentmiddleware deprecated

The behaviour is the same idea: code that runs before a request is matched to a route, so it can redirect, rewrite, or set headers and cookies.

How do you migrate to proxy.ts?

Run the official codemod from your project root:

npx @next/codemod@canary middleware-to-proxy .

Or do it by hand: rename the file, rename the function, keep the config export.

// proxy.ts
import { NextResponse, type NextRequest } from "next/server";

export function proxy(request: NextRequest) {
  const session = request.cookies.get("session")?.value;

  if (!session && request.nextUrl.pathname.startsWith("/dashboard")) {
    return NextResponse.redirect(new URL("/login", request.url));
  }
  return NextResponse.next();
}

export const config = {
  matcher: ["/dashboard/:path*"],
};

How does the matcher work?

The matcher decides which paths run the proxy. Skipping static files keeps it cheap:

MatcherRuns on
"/about/:path*"/about and everything under it
["/dashboard/:path*", "/account/:path*"]Several sections
"/((?!api|_next/static|_next/image|.*\\.png$).*)"Everything except API routes, Next.js assets and PNGs

Matchers can also be objects with source, has and missing conditions, for example to run only when a header or cookie is present.

What should proxy.ts be used for?

Good useAvoid
Redirect logged-out users (cookie check)Full authorization decisions
A/B tests and rewrites (NextResponse.rewrite)Slow database queries
Locale or geo routingHeavy work on every request
Adding security or tracing headersRelying on fetch cache options (they have no effect here)

NextResponse.next() continues to the route, NextResponse.redirect(url) sends the browser elsewhere, and NextResponse.rewrite(url) serves a different route while keeping the URL in the address bar.

Why proxy is not your security layer

Proxy is an optimistic check. A typo in the matcher, a new route, or a direct call to a Server Action can skip it entirely. Server Actions in particular are public POST endpoints.

"use server";

export async function deleteProject(id: string) {
  const session = await auth();                 // real check, every time
  if (!session) throw new Error("Unauthorized");
  await db.project.delete({ where: { id, ownerId: session.userId } });
}

Key takeaway

Rename middleware.ts to proxy.ts (or run the codemod), keep the matcher narrow, and use proxy for fast redirects, rewrites and headers. Do real authorization next to the data. For everything else that changed in Next.js 16, see Next.js 16 caching explained and the free Next.js handwritten notes.

Frequently asked questions

Is middleware.ts deprecated in Next.js 16?

Yes. Starting with Next.js 16, Middleware is called Proxy. The middleware.ts file name and the middleware export are deprecated; rename the file to proxy.ts and export a function named proxy (or a default export).

How do I migrate middleware.ts to proxy.ts?

Run the official codemod with npx @next/codemod@canary middleware-to-proxy . in the project root, or rename middleware.ts to proxy.ts and rename the exported middleware function to proxy. The config and matcher stay the same.

Which runtime does proxy.ts use in Next.js 16?

Proxy runs on the Node.js runtime, and the runtime cannot be changed in the proxy file. Projects that need the edge runtime can keep using middleware for now.

Should I do authentication in proxy.ts?

Only an optimistic check, such as redirecting visitors without a session cookie away from /dashboard. Always verify the session again where data is read or changed: in Server Components, Server Actions and route handlers, because a matcher mistake or a new route can skip the proxy.