In Next.js 16, middleware.ts was renamed to proxy.ts. The idea is still the same: run code before a request is completed. I usually think of Proxy as a checkpoint between the request and your app:
Request → proxy.ts → Next.js route
Proxy can inspect an incoming request and decide what happens next. For example, you can redirect, rewrite URLs, modify headers, or return a response directly. A simple redirect looks like this:
import { NextResponse } from 'next/server'import type { NextRequest } from 'next/server'export function proxy(request: NextRequest) {return NextResponse.redirect(new URL('/home', request.url))}
matcher when neededYou usually don’t want Proxy to run for every route. Use matcher to limit it:
export const config = {matcher: '/admin/:path*',}
Now Proxy runs for /admin and nested routes, which is useful for things like checking a cookie before accessing an admin area.
Proxy is better for lightweight request-level logic such as redirects, rewrites, and quick checks. It shouldn’t become your entire authentication or business-logic layer. Keep the heavy work in your server/API layer.
proxy.ts→ quick request decisionServer / API→ business logic