DEV Community

Usama Habib
Usama Habib

Posted on Originally published at osamahabib.com

Why Next.js Middleware Alone Won't Secure Your App (CVE-2025-29927 + Fix)

There's a dangerous assumption baked into many Next.js apps:

"I check auth in middleware, so my app is protected."

It feels right. Middleware runs before your pages. But middleware alone is not a security boundary - and in 2026 there's a very public reason to take this seriously.

👉 Full security guide here


The 2026 wake-up call: CVE-2025-29927

A critical vulnerability (CVSS 9.1) showed that attackers could completely bypass Next.js middleware authorization by adding a crafted x-middleware-subrequest header to their request.

One header. Middleware skipped. Everything behind it exposed.

It's patched - update to Next.js 14.2.25 / 15.2.3 or later. But the lesson outlives the fix: if middleware is your only defense, one bug exposes everything.


Two other places developers get it wrong

Client-side role checks - hiding an admin button based on role is UX, not security. Anyone can modify client-side JS. A hidden button is still a reachable endpoint.

Server Actions - easily forgotten, but a Server Action can be invoked directly, not just from the button you wired it to. Middleware won't protect it.


The approach that actually works

// 1. Middleware - UX only, not security
export function middleware(req) {
  const hasSession = req.cookies.get("session");
  if (!hasSession) return NextResponse.redirect("/login");
  return NextResponse.next();
}

// 2. Data Access Layer - real guarantee
export const getCurrentUser = cache(async () => {
  const session = await verifySession(); // server-side validation
  if (!session) return null;
  return getUserById(session.userId);
});

// 3. Server Components - re-verify before fetching
const user = await getCurrentUser();
if (!user || user.role !== "admin") redirect("/login");

// 4. Server Actions - authorize independently
export async function deleteUser(id) {
  const user = await getCurrentUser();
  if (user?.role !== "admin") throw new Error("Unauthorized");
}
Enter fullscreen mode Exit fullscreen mode

Each layer assumes the others might fail - that's the point. If middleware is bypassed, the Data Access Layer still refuses to hand over data.


Quick checklist

  • ✅ Update Next.js - patch CVE-2025-29927
  • ✅ Validate session server-side - never trust cookie presence
  • ✅ Re-check auth in Server Components before fetching data
  • ✅ Authorize every Server Action independently
  • ✅ Client-side checks = UX only

Full guide with FAQ on middleware security, CVE details, and the complete defense-in-depth pattern:

👉 osamahabib.com - Next.js Security Guide 2026

Top comments (0)