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.
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");
}
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:
Top comments (0)