Skip to main content
Himanshu Chandola

Next.js 16: Migrating from Middleware to Proxy

|2 min read

Next.js 16 introduces a significant change: the middleware.ts file convention is deprecated in favor of proxy.ts. Here's what you need to know about the migration.

What Changed?

In Next.js 16, the middleware system has been renamed to "proxy" to better reflect its purpose — intercepting and modifying requests before they reach your routes.

The breaking change:

# Old (deprecated)
src/middleware.ts → exports function middleware()

# New (Next.js 16+)  
src/proxy.ts → exports function proxy()

The Error You'll See

If you just rename the file without updating the export, you'll get this error:

⨯ The file "./src/proxy.ts" must export a function, 
either as a default export or as a named "proxy" export.

How to Migrate

Step 1: Rename the file

mv src/middleware.ts src/proxy.ts

Step 2: Rename the exported function

// Before
export async function middleware(request: NextRequest) {
  // ...
}

// After
export async function proxy(request: NextRequest) {
  // ...
}

That's it! The config export with the matcher stays the same.

Full Example

Here's what a complete proxy.ts looks like:

import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

export async function proxy(request: NextRequest) {
  const { pathname } = request.nextUrl;

  // Add security headers
  const response = NextResponse.next();
  response.headers.set("X-Frame-Options", "DENY");
  response.headers.set("X-Content-Type-Options", "nosniff");

  // Rate limiting for API routes
  if (pathname.startsWith("/api/")) {
    // Your rate limiting logic
  }

  return response;
}

export const config = {
  matcher: [
    "/api/:path*",
    "/((?!_next/static|_next/image|favicon.ico).*)",
  ],
};

Why the Change?

The rename makes sense conceptually:

  • Middleware implies something in the middle of request processing
  • Proxy better describes intercepting and forwarding requests

It also aligns with how other frameworks describe this pattern.

⚠️

The old middleware.ts convention still works but will show a deprecation warning. Update now to avoid issues in future versions.

Common Patterns That Still Work

All your existing patterns continue to work — just rename the function:

Rate Limiting

export async function proxy(request: NextRequest) {
  const ip = request.headers.get("x-forwarded-for");
  const { success } = await rateLimiter.limit(ip);
  
  if (!success) {
    return new NextResponse("Too many requests", { status: 429 });
  }
  
  return NextResponse.next();
}

Authentication Checks

export async function proxy(request: NextRequest) {
  const token = request.cookies.get("auth-token");
  
  if (!token && request.nextUrl.pathname.startsWith("/dashboard")) {
    return NextResponse.redirect(new URL("/login", request.url));
  }
  
  return NextResponse.next();
}

Geolocation Headers

export async function proxy(request: NextRequest) {
  const response = NextResponse.next();
  const country = request.geo?.country ?? "US";
  
  response.headers.set("x-user-country", country);
  return response;
}

Vercel Edge Runtime

The proxy runs on Vercel's Edge Runtime by default, giving you:

  • Low latency — runs at the edge closest to users
  • Fast cold starts — typically under 50ms
  • Global distribution — same code runs worldwide

Migration Checklist

  • [ ] Rename middleware.tsproxy.ts
  • [ ] Change export async function middlewareexport async function proxy
  • [ ] Test locally with npm run dev
  • [ ] Deploy and verify no warnings in logs

The change is minimal but will keep your Next.js app aligned with the latest conventions.