You ran npm install next@latest, typed the upgrade command, and watched your terminal explode. Or maybe you just saw the deprecation warning in Next.js 16.1 and felt that familiar pit in your stomach.
It’s not just you. Next.js 16.1 isn't just an upgrade; it’s a paradigm shift.
Vercel has officially renamed middleware.ts to proxy.ts. This isn't cosmetic—it's a hard pivot from the Edge Runtime back to Node.js, and it changes where your authentication logic belongs.
🚀 TL;DR / Full Guide: This article covers the basic syntax migration. For the advanced survival guidecovering Supabase Cookie Syncing, Auth.js v5 wrappers, and Clerk migration patterns, read the full article on my blog:
⚡ The Quick Fix (The Codemod)
If you just want to get your build passing, Vercel provides a codemod to handle the file renaming and export updates.
npx @next/codemod@canary middleware-to-proxy
This will rename your file and change the export from middleware to proxy:
// proxy.ts (formerly middleware.ts)
import { NextRequest, NextResponse } from 'next/server';
// The export must be named 'proxy'
export function proxy(request: NextRequest) {
return NextResponse.next();
}
export const config = {
matcher: ['/dashboard/:path*'],
};
🚨 The "Async Trap" (Why your build might still fail)
Next.js 16.1 aligns with React 19, which means you can no longer access cookies or headers synchronously.
If your old middleware looked like this, it will crash at runtime:
// ❌ BROKEN in 16.1
const cookieStore = cookies();
const token = cookieStore.get('token');
You must update every instance to use await:
// ✅ FIXED
const cookieStore = await cookies();
const token = cookieStore.get('token');
🛑 The "Where Does Auth Go?" Crisis
This is where things get tricky. proxy.ts now defaults to the Node.js runtime. It is designed to be a "Thin Proxy" for redirects and rewrites, NOT for heavy database calls or complex JWT validation.
If you are using Supabase, Auth.js, or Clerk, your authentication flow likely needs a refactor to avoid the "Logout Loop" bug (where users try to sign out but the cookie never clears because the proxy didn't pass the response header).
⚠️ Fixing the Logout Loop: The solution requires creating a mutable response object and manually syncing cookies between the request and response. I've documented the exact code patterns for Supabaseand Auth.js in the full guide.
The New Architecture
To avoid bottlenecks, you need to split your logic:
In
proxy.ts: Perform "optimistic" checks. Does the session cookie exist? If no, redirect to login.In Server Components: Perform the actual validation (database lookups).
Summary
The shift to proxy.ts forces a cleaner architecture, but the migration path is full of undocumented traps regarding authentication libraries.
If you are stuck debugging a specific auth provider, check out the full deep dive on Beyond IT, where I cover the exact implementations for:
✅ Supabase (Fixing the infinite loop)
✅ Auth.js (v5) (The correct wrapper config)
✅ Clerk (The new middleware naming convention)
Top comments (0)