Next.js path · The server side · Topic 8 of 11

Proxy (formerly Middleware)

Not started

Proxy is a single proxy.ts file at the project root (next to app/, or inside src/) whose exported function runs before a request reaches any route. It gets a NextRequest and can let it through, redirect it, rewrite it to a different path while keeping the URL, add or change headers, or return a response itself. Until Next.js 15 this was called Middleware; Next.js 16 renamed it to Proxy to make clear that it sits at the network boundary in front of your app. The old file name and the middleware export are deprecated, and Proxy runs on the Node.js runtime, which you cannot change.

Why it matters: One file can redirect, rewrite or reject requests before any route renders. It is powerful and cheap to misuse: run it on too many paths and it slows everything, trust it for authorisation and one refactor leaves routes open.

  1. 1Renamed in Next.js 16

    `middleware.ts` is deprecated; the file is now `proxy.ts` with an exported `proxy` function. The behaviour is the same, and Proxy runs on the Node.js runtime.

  2. 2Runs before routing

    Proxy sees the request first and can redirect, rewrite to another path, set request or response headers, or answer directly.

  3. 3Match narrowly

    Without a `matcher` it runs on every request, including `_next/static` files, image optimisation and `public/` assets.

  4. 4A filter, not a lock

    Use it for optimistic checks like "no session cookie, go to /login". Real authorisation still belongs in each page, Route Handler and Server Action.

1

How it works

The explanation, with examples you can run.

A typical proxy

// proxy.tsimport { NextResponse, type NextRequest } from "next/server" export function proxy(request: NextRequest) {  const hasSession = request.cookies.has("session")  if (!hasSession && request.nextUrl.pathname.startsWith("/dashboard")) {    const login = new URL("/login", request.url)    login.searchParams.set("next", request.nextUrl.pathname)    return NextResponse.redirect(login)  }   const response = NextResponse.next()  response.headers.set("x-frame-options", "DENY")  return response} export const config = {  // everything except API routes, Next.js assets and common static files  matcher: ["/((?!api|_next/static|_next/image|favicon.ico|.*\\.png$).*)"],}

The matcher is read at build time, so it must be a constant. Only one proxy.ts is allowed; split logic into modules and import them.

What it is good for

  • Redirects and rewrites that depend on the request: locale detection, A/B buckets, legacy URLs. (Fixed redirects are simpler in next.config.ts.)
  • Headers for many routes at once, such as security headers or a request ID.
  • Optimistic auth checks: is there a session cookie at all? Send obvious anonymous traffic to the login page before rendering anything.

What it is not for

Proxy runs on every matched request, so anything slow in it slows every page. It isn't the place for database queries or full session validation, and fetch cache options have no effect inside it. More importantly, it is not an authorisation layer. A page can be reached through a rewrite, a Server Action is a POST to whatever route renders it, and a later change to the matcher pattern can exclude paths you meant to protect without any error. The Data Security guidance is to check authentication and authorisation where data is read or changed: in a data access layer used by Server Components, Route Handlers and Server Actions.

If you were using Middleware with runtime = "edge", note that Proxy does not support the Edge runtime. The upgrade guide says to keep the old middleware file for now if you depend on Edge, but the Edge runtime itself is deprecated for routes in current Next.js, so plan to move that code to Node.js.

2

Check yourself

3 questions. Get them all right to finish this topic.

  1. 1.You upgrade to Next.js 16. What should `middleware.ts` become?
  2. 2.Proxy redirects users without a session cookie to `/login` and has no matcher. After deploying, the login page has no styles. Why?
  3. 3.Is a session check in Proxy enough to protect a Server Action?

0 of 3 right so far. Get all three to finish this topic.