Skip to main content

Command Palette

Search for a command to run...

From middleware.ts to proxy.ts: What Changed in NextJS 16

What is new in NextJS 16 Proxy

Updated
7 min read
From middleware.ts to proxy.ts: What Changed in NextJS 16
G

I am a software developer with a passion for frontend technologies using vanilla HTML, CSS, JavaScript, and frameworks like Bootstrap and React. I also write Backend for websites sometimes using Python and Django.

Next.js has just released Next.js v16.0.0, which comes with several interesting changes, one of which is the name change from middleware.ts to proxy.ts. The official announcement article claimed it’s the right naming convention, as different technologies have different meanings and implementations of middleware, and Next.js middleware is practically a proxy, so why not name it what it is? But is the name the only change? What else changed alongside the name? Find out in this article.

A Quick Refresher: Proxy vs. Middleware

Middleware, as its name implies, is software that sits between two applications or web services, handling communication and data management between them. In web development, it is a function that runs after a server receives a request, but before the final route handler sends a response. Think of middleware like a security guard and a receptionist at an organisation. First, the security checks to know if you’re safe to enter the premises, then they direct you to the receptionist (another middleware). The receptionist receives you, checks if you have an appointment, and upon confirmation, directs you to the office of the person you seek (data). Middleware forms a chain, and a request might go through several links before its final destination.

A proxy, or server proxy, on the other hand, is a server-level intermediary between networks. It acts as a gateway between a device’s network and the internet. Think of it like a secure mail forwarding service. Instead of allowing every website to see your home address (IP address), you forward your mail to the forwarding service (proxy). The service scrapes off your home address from the envelope and replaces it with its own, then sends it to the destination (website). When the receiver sends feedback, since it doesn’t have your home address, it sends it to the forwarding service, which, in turn, sends it to your home address. A proxy provides privacy and anonymity, security, helps bypass restrictions, and improves performance via caching.

So, is proxy.ts a Proxy or Middleware?

Next.js introduced middleware in v.12.0.0 to mimic backend middlewares. The primary purpose of Next.js middleware was to intercept and modify requests before they reach a specific API route. It provided developers with a single, centralised solution to run logic on the server, rather than handling multiple units of tasks, such as authentication or redirects inside getServerSideProps for every protected page, or using custom server solutions. The middleware handled authentication and authorisation, redirects and rewrites, internalization (i18n) logic, logging and analytics, and modifying request headers from a single file. However, it ran on the Edge runtime, a lightweight JavaScript environment, which made it extremely fast but limited its ability to use native Node.js APIs.

So, where does the Next.js new proxy.ts file fit in all these explanations? If you’re coming from an Express.js background and have been using Next.js for a while, you would not be wrong to assume that the proxy.ts file is still a middleware file. In fact, it is, and the proxy section of Next.js documentation confirmed it by maintaining most of the usual description and implementation of the former middleware. Next.js even suggested an organisation module for each logic in the proxy file, where each module is a function that handles a single proxy functionality—the exact way middlewares are handled in Express.js. And while you wouldn’t be wrong, you wouldn’t exactly be right either, because there is an addition that changes everything. The truth is, the proxy.ts file acts as a "middleware" (it's inside your app) but performs a "proxy" role (intercepting and routing network requests).

The Real Change: It’s The Runtime

It turns out that the name change from middleware.ts to proxy.ts isn’t just for aesthetics. It’s a signal for a practical change: the runtime environment where your code executes. Unlike middleware.ts, the proxy.ts file runs on the Node.js runtime engine, giving it access to native Node.js APIs. This is the most significant update of this change, not the name. To better understand it, let’s observe the “before” and “after.”

The "Old" middleware.ts (Edge Runtime)

The original Next.js middleware was designed to be fast and lightweight, so it was made to run on the Edge runtime.

  • The Good: The Edge runtime is a high-speed, minimal JavaScript environment that can run globally, close to your users. This made it perfect for simple, speedy tasks, such as handling redirects, setting cookies, or rewriting URLs for internationalization (i18n).

  • The Bad: It's a sandbox. It only supports web-standard APIs (like fetch and the Web Crypto API). It has no access to native Node.js APIs like fs (file system), path, or os. This was a major limitation. If you needed to read a file or use a Node-only npm package, you simply couldn't.

The "New" proxy.ts (Node.js Runtime)

This new file convention tells Next.js to run your code on the standard Node.js runtime.

  • The Good: This is a massive change. You now have access to the entire Node.js API set. You can read from or write to the file system, use native Node modules, and, most importantly, import almost any npm package from the Node.js ecosystem that was previously incompatible with the Edge.

  • The Bad: This power comes with a trade-off. The Node.js runtime isn't as fast to start up (it has a "cold start") and typically runs as a standard serverless function in a single region, unlike Edge, which runs globally.

This isn't a replacement; it's a new option. The rename helps you, the developer, make an explicit choice. When you select either filename (proxy.ts or middleware.ts), you’re telling Next.js which runtime you want.

So, what new, practical things can you actually do with this full Node.js access?

What This Unlocks: The Power of Node.js in Your Proxy

Running proxy.ts on the Node.js runtime isn't just a technical detail; it's a massive upgrade that opens up a new world of server-side capabilities previously impossible in the old middleware.ts. You now have the entire Node.js API set and the Node-compatible npm ecosystem at your disposal. Let's look at what this means in practice.

Practical Example 1: Dynamic Redirects with the File System (fs)

This is the most powerful and obvious new capability. You can now directly read from and write to the server's file system.

Imagine you need to manage thousands of redirects for your e-commerce site, but you don't want to hard-code them in your proxy.ts file or make a slow database call for every request.

With the Node.js runtime, you can just read a local JSON file.

// src/proxy.ts

import { NextResponse } from 'next/server';

import type { NextRequest } from 'next/server';

import { readFileSync } from 'fs';

import { join } from 'path';


// This is the new part:

// 1. Get the path to a JSON file in your project

const redirectsPath = join(process.cwd(), 'redirects.json');

let redirects = {};


// 2. Read the file FROM THE DISK and parse it

try {

  const file = readFileSync(redirectsPath, 'utf8');

  redirects = JSON.parse(file);

  // Your redirects.json might look like:

  // { "/old-product-1": "/new-product-page", "/legacy-blog": "/blog" }

} catch (error) {

  console.error("Could not read redirects file:", error);

}


export function proxy(request: NextRequest) {

  const { pathname } = request.nextUrl;


  // 3. Check the path against the list from your file

  const redirectUrl = redirects[pathname];



  if (redirectUrl) {

    const newUrl = new URL(redirectUrl, request.url);

    return NextResponse.redirect(newUrl.toString());

  }


  // Let the request continue

  return NextResponse.next();

}

This simple operation was impossible in the Edge runtime middleware.ts. You had no access to fs, path, or process.cwd(). Your only options were to hard-code the redirects or fetch them from an external CMS or database, adding latency and complexity.

Practical Example 2: Using Node-only npm Packages

The other major win is compatibility. Many of the most powerful and popular libraries in the npm ecosystem were built only for the Node.js environment because they rely on native APIs.

Using the Edge runtime (middleware.ts), installing a package like node-postgres (a database driver) or ioredis (a Redis client) would cause your application to crash instantly.

Using the Node.js runtime (proxy.ts), they just work.

This unlocks a range of new use cases for your proxy, such as:

  • Database Connections: Connect directly to a database (like Postgres, MySQL, or Redis) to validate an API key, check feature flags, or perform advanced routing logic.

  • Complex Auth: Implement more advanced authentication strategies using Node-first libraries.

  • Server-Side Analytics: Use a Node-compatible analytics library to log request data directly from the proxy.

So, with all this power, proxy.ts seems like the obvious choice for everything. Besides, the Next.js team has deprecated middleware with the release of version 16, and soon enough, it’ll be completely removed. It’s a new era, and time will tell if it’s worth it.

Conclusion

So, is the switch from middleware.ts to proxy.ts just a name change? Absolutely not.

While the announcement about "the right naming convention" might have seemed like a simple aesthetic update, it's one of the most practical changes in Next.js 16. The rename isn't the real story; it's the signpost pointing to the real story: the official introduction of the Node.js runtime as the new option for your request interception logic. If you’ve been waiting for this update for a very long time, now is your time to shine.

Cover Image from Next