Skip to the solution
MediumFrontend9 min read

What is proxy.ts in Next.js 16 and how do you migrate from middleware.ts?

1.1k views

The problem

Question presented to candidate: "Our app has a middleware.ts that redirects logged-out users, rewrites a few legacy URLs and adds a request ID header. Next.js 16 talks about proxy.ts instead. What is it, what exactly do we change, and what could go wrong in the migration?"

What a strong answer should cover:

  • proxy.ts is the same feature under a new name: a function at the project root (or src/) that runs before routing for every matched request and can redirect, rewrite, set headers or respond.
  • Migration: rename the file and the exported function (middleware to proxy), rename config flags such as skipMiddlewareUrlNormalize; the middleware-to-proxy codemod does both.
  • Proxy always runs on the Node.js runtime and a runtime export is an error; middleware.ts is deprecated but still works and still defaults to Edge, so renaming changes the runtime.
  • Keep it thin and use config.matcher; do authorization inside each page, Route Handler and Server Action as well, because a matcher change can silently remove coverage.
  • Having both files is a build error; a proxy.ts that still exports middleware is a build error too.

Clarifying questions expected:

  • "Does our middleware depend on the Edge runtime or run on a platform's edge network?" β€” the guide says to keep middleware.ts in that case.
  • "What does it actually do?" β€” redirects and headers often belong in next.config instead.

Code / implementation expected: Yes β€” a proxy.ts with an auth redirect, a rewrite, a header and a matcher.

  • nextjs
  • proxy
  • middleware
  • next-16

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

9 min read Β· 8 sections

Read the code

proxy.ts with a redirect, a rewrite and headers
import { NextResponse, type NextRequest } from "next/server";

export function proxy(request: NextRequest) {
  const { pathname } = request.nextUrl;

  // 1. Optimistic auth check: redirect early if there is no session cookie
  if (pathname.startsWith("/dashboard") && !request.cookies.has("session")) {
    return NextResponse.redirect(new URL("/login?next=" + pathname, request.url));
  }

  // 2. Rewrite an old URL to a new page without changing the address bar
  if (pathname === "/docs") {
    return NextResponse.rewrite(new URL("/p", request.url));
  }

  // 3. Pass through, adding a request header for the page and a response header
  const headers = new Headers(request.headers);
  headers.set("x-request-id", crypto.randomUUID());
  const response = NextResponse.next({ request: { headers } });
  response.headers.set("x-proxy", "hit");
  response.headers.set("x-proxy-runtime", typeof (globalThis as { EdgeRuntime?: string }).EdgeRuntime === "string" ? "edge" : "nodejs");
  return response;
}

export const config = {
  matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};
Typed proxy with NextProxy and a default export
import { NextResponse } from "next/server";
import type { NextProxy } from "next/server";

const proxy: NextProxy = (request) => {
  if (request.nextUrl.pathname.startsWith("/admin") && !request.cookies.has("session")) {
    return NextResponse.redirect(new URL("/login", request.url));
  }
  return NextResponse.next();
};

export default proxy;

export const config = { matcher: ["/admin/:path*"] };
Migration commands
# rename middleware.ts -> proxy.ts and middleware() -> proxy()
npx @next/codemod@canary middleware-to-proxy .

# or by hand
mv middleware.ts proxy.ts
# then change: export function middleware(...)  ->  export function proxy(...)
# and in next.config: skipMiddlewareUrlNormalize -> skipProxyUrlNormalize

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 27 of 129 in Next.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Next.js questions
Up nextMedium

How does Next.js decide between static and dynamic rendering at build time?

Continue