Why Next.js is Moving Away from Middleware: Understanding the proxy.ts Rename
The security vulnerability that forced Next.js to rethink where authentication belongs

⚡ Next.js Implementation Guides
In-depth Next.js guides covering App Router, RSC, ISR, and deployment. Get code examples, optimization checklists, and prompts to accelerate development.
When Next.js 16 Beta announced the deprecation of middleware.ts
in favor of proxy.ts
, the developer community reacted with confusion and frustration. Reddit threads filled with complaints: "This makes things more confusing, not less." "A proxy is nginx or Traefik, not this." "Why rename something that works fine?"
But here's what most developers missed: this isn't just a rename. It's Next.js fundamentally rethinking where security boundaries should exist in your application. And if you understand why they're making this change, it actually reveals something important about building secure web applications in serverless and edge environments.
The Vulnerability That Changed Everything
In March 2025, security researcher Rachid Allam disclosed a critical vulnerability in Next.js middleware that affected every version from 11.1.4 through 15.2.2. The exploit was elegant and devastating: by adding a simple x-middleware-subrequest
header to an HTTP request, an attacker could completely bypass all middleware-based authorization checks.
Think about that for a moment. If your Next.js app used middleware to protect routes, verify authentication, or enforce access controls, an attacker could skip past all of it with a single header. Every protected admin panel, every authenticated API route, every authorization check—all rendered useless.
The vulnerability wasn't just a bug in the implementation. It exposed something more fundamental: using middleware as your security boundary is inherently fragile in Next.js's architecture. The framework's execution model, which runs code across edge functions and serverless environments, creates attack surfaces that don't exist in traditional server frameworks.
Next.js patched the specific vulnerability, but they realized something bigger: they needed to change how developers think about security in their framework. The middleware.ts
→ proxy.ts
rename is their way of doing that.
What Next.js is Actually Trying to Achieve
When the Next.js team says the rename is about "clarifying network boundary and routing focus," they're being diplomatic. What they're really saying is: stop putting auth in middleware, it's dangerous in our execution model.
Let's break down their intended architectural shift:
The Old Mental Model:
- Middleware acts as a gatekeeper for your entire application
- You define protected routes once at the top level
- If a request makes it past middleware, it's considered safe
- All your auth logic lives in one centralized file
The New Mental Model:
- Proxy handles routing, rewrites, and redirects only
- Authentication and authorization happen at the data layer
- Every protected resource verifies access independently
- Security checks live alongside the code they protect
By renaming the file to proxy.ts
, Next.js is trying to signal: "This is infrastructure for routing, not a security mechanism." They want you to think of it like nginx or a CDN edge function—something that routes traffic but doesn't make security decisions.
The Intended Benefits of This Approach
Next.js believes this architectural shift provides several key advantages, and understanding them helps clarify why they're pushing this change so hard.
Benefit 1: Defense in Depth
When you centralize all your auth in middleware, you create a single point of failure. If that middleware can be bypassed—as the March vulnerability proved—your entire application is compromised. The attacker doesn't need to exploit each protected route individually; they just need to slip past the one guard at the door.
By moving auth checks to the data layer, you eliminate this single point of failure. Even if an attacker somehow bypasses your routing logic, they still can't access protected data without passing the verification checks that live directly alongside your database queries and server actions.
This is the "defense in depth" security principle: multiple layers of verification, so that no single failure compromises your entire system.
Benefit 2: More Explicit and Auditable Security
When auth is "magical" middleware that protects routes automatically, it's easy to make mistakes. You might forget which routes are protected, or assume protection exists where it doesn't. The security model is implicit—you have to understand the middleware configuration to know what's safe.
When you verify access at the point of data retrieval, security becomes explicit. Looking at your code, you can immediately see: "This function checks authentication before returning user data." There's no mystery about whether a route is protected—the protection is right there in the code.
This also makes security audits dramatically easier. Instead of tracing through middleware configuration files and route matching logic, you can audit each protected resource directly. If a function accesses sensitive data, you can see exactly what security checks it performs.
Benefit 3: Better Performance Characteristics
Middleware in Next.js runs on every single request that matches its path configuration. Even for public routes, even for static assets, the middleware executes. This has performance implications, especially in edge and serverless environments where cold starts and execution time directly impact your bill and user experience.
By moving auth checks closer to where they're actually needed, you can optimize more aggressively. Public pages don't need to run any auth logic at all. Protected pages only verify auth when they actually fetch protected data. You can memoize session verification across multiple data fetches in the same request without worrying about middleware execution order.
Benefit 4: Safer in Distributed Execution Models
Here's the part that's specific to Next.js's architecture: their framework runs code across edge functions, serverless functions, and even client-side environments. This distributed execution model is what makes Next.js powerful for global performance, but it also creates unique security challenges.
Traditional middleware assumes a single server process handling requests from start to finish. But in Next.js, a single request might trigger multiple separate function invocations across different environments. Middleware runs in one execution context, your API routes in another, your server components in yet another.
This distributed model makes it harder to guarantee that middleware protections actually apply to downstream execution. The March vulnerability exploited exactly this gap—the ability to mark requests as "internal" and bypass the middleware layer entirely.
By requiring explicit verification at each data access point, Next.js ensures that security checks happen in the same execution context as the sensitive operation. There's no opportunity for requests to slip between execution boundaries.
Why the Community Pushback Makes Sense
The frustration in the Reddit thread is completely understandable. Experienced developers are right to be confused by calling this feature a "proxy."
In the broader web development world, a proxy is a separate infrastructure component: nginx, Traefik, Cloudflare, HAProxy. These are network-level tools that sit between clients and servers. Calling Next.js's routing layer a "proxy" conflicts with decades of established terminology.
More importantly, the pattern Next.js is moving away from—centralized route guards—works perfectly fine in other frameworks. Remix has loaders with authentication. SvelteKit has hooks that protect routes. Nuxt has middleware that verifies sessions. These frameworks haven't had the security issues Next.js experienced.
The difference is execution model. Those frameworks run in more traditional server environments where middleware really does act as a reliable gatekeeper. Next.js's edge and serverless architecture introduces complexities that make the same pattern less reliable.
But here's where the community has a valid complaint: Next.js chose to solve this with confusing terminology rather than fixing the underlying architectural issues. Instead of making middleware truly reliable in their execution model, they're telling developers to stop using it for security. And they're using a name that conflicts with existing conventions to drive that message home.
What This Means for the Next.js Ecosystem
Whether you agree with the approach or not, this shift will reshape how we build Next.js applications. Auth libraries like NextAuth (now Auth.js), Clerk, and custom solutions will need to adapt their patterns away from middleware-first approaches.
We'll see more tutorials teaching "verify at the data layer" as the primary pattern. New developers learning Next.js won't even think about putting auth in middleware—they'll learn to verify sessions in their server actions and API routes from day one.
The proxy.ts
file will become what Next.js always intended it to be: a place for routing logic, URL rewrites, and redirects. Not a security mechanism, but a traffic routing layer.
This will likely make Next.js applications more secure by default, even if the path to get there feels awkward. By forcing developers to be explicit about security at every access point, Next.js reduces the risk of forgotten checks or bypassed protections.
The Bigger Picture
The middleware.ts
→ proxy.ts
rename isn't about clarity—it's Next.js's way of steering the ecosystem away from a security pattern they've learned is dangerous in their execution model. The March 2025 vulnerability was a wake-up call that their architecture requires different security thinking than traditional server frameworks.
Is "proxy" the right name for this? Probably not—it conflicts with too much existing terminology. But the underlying principle is sound: in distributed, serverless execution environments, you can't rely on a single gatekeeper to protect everything. You need verification at every access point.
As Next.js 16 moves from beta to stable release, we'll see whether this architectural philosophy catches on or whether the community finds ways to work around it. But understanding why they're making this change—not just what they're changing—helps you make better architectural decisions for your own projects.
The real lesson isn't about Next.js specifically. It's about how different execution models require different security patterns. What works in a traditional server might not work in edge functions. What's convenient might not be secure. And sometimes, the framework's job is to make the secure pattern the easy pattern, even if that means breaking with conventions.
Let me know in the comments what you think about this shift. Are you convinced by Next.js's reasoning, or do you think they should have solved this differently? Subscribe for more deep dives into web framework architecture and security patterns.
Thanks, Matija