Next.js75 min total · 12 parts
Next.js Fundamentals: The App Router, Server Components, and the Caching Rules That Just Flipped
Part 10 of 12 · ~3 min
Middleware
Worth stating plainly before anything else: as of Next.js 16, this file is proxy.ts, not middleware.ts. A lot of what's out there about this topic still uses the old name. The underlying behavior didn't change — Next.js's own release notes describe the rename as cosmetic, "the functionality remains the same" — but the file itself, the function you export from it, and the environment it runs in by default all changed together. middleware.ts isn't gone; it still works today, but it's the older, Edge-runtime-only form, marked for eventual removal. Everything below uses the current file.
proxy.ts, sitting at the project root, runs before a request ever reaches a route — a page, a Route Handler, anything:
// proxy.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function proxy(request: NextRequest) {
const session = request.cookies.get("employer_session")?.value;
if (!session && request.nextUrl.pathname.startsWith("/dashboard")) {
return NextResponse.redirect(new URL("/login", request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ["/dashboard/:path*"],
};
That's the most common thing it does in practice — turning away a signed-out visitor from /dashboard before a single byte of that page renders. Two other shapes show up almost as often: running an A/B test, sending a slice of homepage traffic to an alternate hero headline based on a cookie, invisibly, with the visitor's URL never changing; and region-aware logic — reading a geo header to decide whether OpenRoles should show a salary band in dollars or in a visitor's local currency, before either version ever gets rendered.
What's actually still off-limits here — and what isn't anymore
One correction worth being precise about: the live version of this reference states a constraint that Next.js 16 has already partly undone. proxy.ts runs on the Node.js runtime by default now, not the cramped Edge runtime that middleware.ts was permanently locked into. That's a genuine, shipped change, not a footnote — the old blanket rule of "no Node APIs, no reaching a database directly" doesn't automatically hold for Proxy the way it held for Edge-only Middleware.
What hasn't changed, and matters far more day to day than the runtime question does: Next.js's own documentation is direct about Proxy not being built for slow work, and it specifically says it "should not be used as a full session management or authorization solution." A cheap, optimistic check belongs here — does a plausibly valid session cookie even exist — while the real question, the one that actually needs a database ("does this employer genuinely own this listing"), belongs down in the route or the Server Action itself, which runs on every request regardless of what Proxy already decided. One rule worth carrying forward from a couple of chapters back, because it's specific and easy to trip over: the cache, next.revalidate, and next.tags fetch options from chapter 5 do nothing inside Proxy at all — any fetch call made in here is dynamic, full stop, no matter what you pass it.
If OpenRoles ever genuinely needs the Edge runtime specifically — physical proximity to the visitor, before the request has to travel to wherever the main app actually runs — that option is still there. It's just no longer the only one, or the default one, the way older material describes it.