If you just upgraded to Next.js 16 and your app exploded, you are not alone.
The most controversial breaking change in this release is the complete removal of middleware.ts. It has been replaced by a new primitive: proxy.ts.
And itβs not just a rename. The rules have changed.

Why did they kill Middleware?
For years, developers abused Middleware. We engineered complex authentication flows, database calls, and heavy logic into a layer that was only meant for routing.
This came to a head with CVE-2025-29927, a vulnerability where Middleware authentication could be bypassed under high load due to Edge Runtime limitations.
Vercel's response in Next.js 16 is clear: Network boundaries must be explicit.
proxy.tsruns on Node.js (by default), not the limited Edge Runtime.- It is strictly for Routing (Rewrites, Redirects, Headers).
- It is NOT for Authentication (Auth should happen in Layouts or Route Handlers).
The Migration: middleware.ts vs proxy.ts
1. File Rename & Location
- Old:
middleware.tsin root orsrc/. - New:
app/proxy.ts(Must be inside the App Router directory).
2. Syntax Changes
The signature looks similar, but notice the function name and runtime behavior.
The Legacy Way (middleware.ts):
// β middleware.ts (Legacy)
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// We used to do Auth here... dangerous!
const token = request.cookies.get('token')
if (!token) return NextResponse.redirect(new URL('/login', request.url))
return NextResponse.next()
}The Next.js 16 Way (proxy.ts):
// β
app/proxy.ts
import { ProxyRequest, ProxyResponse } from 'next/server'
// Note: It's named 'proxy', not 'middleware'
export async function proxy(request: ProxyRequest): Promise<ProxyResponse> {
const { pathname } = request.nextUrl
// 1. Rewrites (A/B Testing, Localization)
if (pathname === '/about') {
return ProxyResponse.rewrite(new URL('/fr/about', request.url))
}
// 2. Redirects (Legacy paths)
if (pathname.startsWith('/old-blog')) {
return ProxyResponse.redirect(new URL('/blog', request.url))
}
// 3. Headers (Security)
// You can now access full Node.js APIs here if needed!
const res = ProxyResponse.next()
res.headers.set('X-Frame-Options', 'DENY')
return res
}[!WARNING] Do not attempt to read databases or verify complex JWTs in
proxy.ts. While it runs on Node.js and technically can connect to a DB, blocking the request at the proxy level adds significant latency to your TTFB (Time To First Byte) for every single request.
Where does Auth go now?
If proxy.ts is just for routing, where do we protect our routes?
Next.js 16 introduces Server Layout Guards.
Instead of a global middleware file, you wrap your protected routes in a layout.tsx that performs the check. This leverages React Server Components (RSC) to handle security closer to the data.
// app/dashboard/layout.tsx
import { redirect } from 'next/navigation'
import { verifySession } from '@/lib/auth' // Your standard server-side auth
export default async function DashboardLayout({ children }) {
const session = await verifySession()
if (!session) {
redirect('/login') // Server-side redirect
}
return (
<section>
{children}
</section>
)
}Production Snippets for proxy.ts
Here are the safe, approved patterns for the new Proxy layer.
1. The Localizer (Geo-Routing)
Since proxy.ts has full Node.js access, you can use powerful libraries like maxmind directly if you want, but the standard request.geo (on Vercel) is still the fastest.
export function proxy(request: ProxyRequest) {
const country = request.geo?.country || 'US'
const locale = getLocaleFromCountry(country) // e.g., 'fr-FR'
// Transparent rewrite
return ProxyResponse.rewrite(new URL(`/${locale}${request.nextUrl.pathname}`, request.url))
}2. Header Injection (CSP & Security)
export function proxy(request: ProxyRequest) {
const nonce = crypto.randomUUID() // Valid Node.js crypto!
const headers = new Headers(request.headers)
headers.set('x-nonce', nonce)
const response = ProxyResponse.next({
request: { headers }
})
response.headers.set('Content-Security-Policy', `script-src 'self' 'nonce-${nonce}'`)
return response
}Summary
The death of middleware.ts is painful but necessary. It forces us to decouple Routing (Proxy) from Security (Layouts/RSC).
Checklist for migration:
- [ ] Rename
middleware.tstoapp/proxy.ts. - [ ] Rename exported function to
proxy. - [ ] CRITICAL: Move all Authentication logic OUT of the proxy and into
layout.tsxor Server Actions. - [ ] Celebrate your faster, more secure app.
Happy coding, and welcome to the Next.js 16 era. π
References & Further Reading
For those who want to verify the details or read the full specs, here are the official sources:
- CVE-2025-29927: The vulnerability details regarding Middleware authentication bypass.
- Next.js 16 Upgrade Guide: The official codemod for migrating to
proxy.ts. - Vercel Proxy Documentation: Deep dive into the Node.js Proxy runtime capabilities.
