Next.js's 2026 security advisories all say the same thing: middleware is not a security boundary
Vercel started shipping Next.js security fixes on a scheduled monthly cadence in 2026, and reading through a year's worth of advisories back to back surfaces something more useful than any single CVE: a repeating category of bug. Middleware and proxy redirect handling has been bypassed, cache-poisoned, and patched, then found bypassable again through a slightly different path. One advisory is literally titled "Incomplete Fix Follow-Up" — the first patch for a middleware bypass in App Router applications didn't fully close the hole, and a second advisory had to correct it.
That's not a knock on the Next.js team; shipping fixes fast and publishing follow-ups when a fix turns out incomplete is exactly the right behavior. But the pattern is worth sitting with, because a lot of App Router codebases are built on an assumption these advisories keep quietly disproving: that middleware is where you enforce security.
What actually got bypassed
A few of 2026's advisories, in plain terms:
- Middleware/proxy redirects could be cache-poisoned. A response meant for one request gets cached and served to a different user, because the cache layer and the middleware layer disagreed about what varied the response.
- Middleware/proxy bypass in Pages Router apps using i18n. The locale-routing logic gave attackers a path that skipped the middleware check entirely for certain URL shapes.
- Middleware/proxy bypass in App Router apps via segment-prefetch routes — patched, then re-patched, because the first fix didn't account for every route shape prefetching could hit.
- SSRF via WebSocket upgrade requests in self-hosted deployments — the built-in Node server's connection-upgrade path didn't go through the same request validation as normal HTTP requests.
Read individually, these are framework bugs to patch and move on from. Read together, they describe one structural problem: any system that inspects a request before routing decides how it will actually be handled has to anticipate every way that routing can be reached — including edge cases the original author of the middleware never tested, like a prefetch request, a WebSocket upgrade, or a locale-prefixed URL.
The pattern in your own code
This matters beyond Next.js's own bugs because the same shape of mistake shows up in application code every time someone puts an authorization check exclusively in middleware:
// middleware.ts
export function middleware(request: NextRequest) {
const token = request.cookies.get("session")?.value;
if (!token || !isValidSession(token)) {
return NextResponse.redirect(new URL("/login", request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ["/dashboard/:path*"],
};
This looks like a real access control layer, and for the common case — a browser navigating to /dashboard — it is one. But a matcher config is a routing pattern, not a proof. Server Actions, route handlers hit directly, prefetch requests, and any path that doesn't match the matcher's exact shape can reach the underlying logic without ever passing through this check. That's precisely the category of bug Next.js has spent 2026 patching in its own routing layer — the framework's edge cases are a preview of your app's edge cases.
The actual fix: push the check to where the data lives
The advisories all resolve to the same architectural lesson: treat middleware as a fast-path UX optimization — redirect unauthenticated users before they see a flash of protected content — and put the real authorization check next to the data access itself, where there's no routing shape left to misconfigure:
// app/dashboard/page.tsx
export default async function DashboardPage() {
const session = await getSession(); // re-verified here, not trusted from middleware
if (!session) redirect("/login");
const projects = await getProjectsForUser(session.userId); // scoped at the query
return <ProjectList projects={projects} />;
}
async function getProjectsForUser(userId: string) {
return db.project.findMany({ where: { ownerId: userId } }); // the actual boundary
}
Middleware redirecting an unauthenticated browser is good UX. getProjectsForUser refusing to return another user's data regardless of how the request arrived is the actual security boundary. If those two checks ever disagree — because a route bypassed the matcher, a prefetch hit an edge case, or a future refactor changes the matcher pattern — the data layer is what keeps a bypass from becoming a breach.
What to actually do about it
Subscribing to Next.js's security advisories (github.com/vercel/next.js/security/advisories) and updating on the monthly cadence is table stakes — several of 2026's fixes closed real, exploitable holes and there's no reason to run three months behind on them. But the advisories are also worth reading past the changelog line, because the underlying pattern — a request-inspection layer that assumed it would only ever see the request shapes its author imagined — is exactly the failure mode to audit for in your own middleware, API gateways, and reverse proxies, Next.js or not. Any place in a system where "check first, then route" is the whole security model is a place where the next edge case, not the current one, is the actual risk.