Goodbye middleware.ts, Hello proxy.ts: The Next.js 16 Migration Guide
Writing
NEXT.JS MASTERY
December 20, 20255 min read

Goodbye middleware.ts, Hello proxy.ts: The Next.js 16 Migration Guide

Next.js 16 has killed `middleware.ts`. Learn how to migrate to `proxy.ts`, why auth in middleware is now considered unsafe, and how to master the new Node.js-based proxy layer.

nextjs 16proxy.tsweb-developmentreactsecuritybreaking-changes

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.

Middleware to Proxy Migration

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.ts runs 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).

How do you migrate from middleware.ts to proxy.ts in Next.js 16?

1. File Rename & Location

  • Old: middleware.ts in root or src/.
  • 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>
  )
}

What are production-ready proxy.ts patterns for Next.js 16?

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:

  1. Rename middleware.ts to app/proxy.ts.
  2. Rename exported function to proxy.
  3. CRITICAL: Move all Authentication logic OUT of the proxy and into layout.tsx or Server Actions.
  4. 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:

Keep Reading

Frequently Asked Questions

Why was middleware.ts removed in Next.js 16?

middleware.ts was removed because developers abused it for complex authentication and database operations that were only meant for routing. The critical CVE-2025-29927 vulnerability demonstrated that Edge Runtime middleware could be bypassed under load, making authentication checks unreliable. proxy.ts replaces it with a Node.js-based routing layer.

What is proxy.ts in Next.js 16?

proxy.ts is the replacement for middleware.ts in Next.js 16. It lives at app/proxy.ts and runs on Node.js (not Edge Runtime). It handles routing concerns like rewrites, redirects, and header injection. Unlike middleware.ts, it explicitly separates routing from authentication logic.

How do you handle authentication without middleware.ts in Next.js 16?

Authentication in Next.js 16 should be handled in Server Layout Guards — layout.tsx files that verify the session using React Server Components. This moves security checks closer to the data layer instead of the network edge, making them more reliable and harder to bypass.

Last updated: April 2, 2026

Rabinarayan Patra

Rabinarayan Patra

SDE II at Amazon. Previously at ThoughtClan Technologies building systems that processed 700M+ daily transactions. I write about Java, Spring Boot, microservices, and the things I figure out along the way. More about me →

X (Twitter)LinkedIn

Stay in the loop

Get the latest articles on system design, frontend & backend development, and emerging tech trends — straight to your inbox. No spam.