Summary
A Next.js app cannot read HttpOnly cookies directly, so the frontend cannot decide whether a user is authenticated. The correct pattern is to let Next.js Middleware or server components validate the session by forwarding the cookies to your backend and letting the backend confirm whether the token is valid. The browser sends cookies automatically, so the server layer can still perform full authentication checks even though the client cannot read them.
Root Cause
The confusion comes from assuming that authentication must be checked in the browser. With HttpOnly cookies, the browser cannot access token values, but the server still can because cookies are included in every request.
Key points:
- HttpOnly cookies are invisible to JavaScript, but they are still sent with every request.
- Next.js Middleware runs on the server, not the client.
- Middleware can forward cookies to the backend to validate the session.
- The backend remains the source of truth for token validity.
Why This Happens in Real Systems
Real production systems use HttpOnly cookies because:
- They prevent XSS token theft.
- They enforce server-side session validation.
- They centralize authentication logic in the backend.
This means:
- The frontend is intentionally “blind” to token contents.
- All meaningful auth checks must happen server-side.
Real-World Impact
If you try to validate auth on the client:
- You cannot read the cookie, so you cannot know if the user is authenticated.
- Expired tokens cannot be detected until the backend rejects a request.
- Protected pages may flash before redirecting because the client guesses incorrectly.
If you validate auth on the server:
- No UI flicker because redirects happen before rendering.
- Expired/invalid tokens are caught early.
- Security is stronger because the client never handles tokens.
Example or Code (if necessary and relevant)
A common pattern is to call your backend from middleware:
// middleware.ts
import { NextResponse } from 'next/server';
export async function middleware(req) {
const res = await fetch('https://api.example.com/auth/validate', {
headers: { cookie: req.headers.get('cookie') || '' },
});
if (res.status !== 200) {
return NextResponse.redirect(new URL('/login', req.url));
}
return NextResponse.next();
}
How Senior Engineers Fix It
Senior engineers rely on server-side validation and avoid client-side token logic entirely.
They typically:
- Use middleware to protect routes by calling the backend.
- Use server components or route handlers to fetch user data securely.
- Let the backend handle:
- token expiration
- token rotation
- session invalidation
- Keep the frontend stateless and unaware of token details.
They also ensure:
- Refresh tokens are rotated server-side.
- Access tokens are short-lived.
- Middleware caches validation results when possible to reduce backend load.
Why Juniors Miss It
Juniors often assume:
- Authentication must be checked in the browser.
- Tokens must be readable by JavaScript.
- Middleware needs to decode JWTs directly.
They miss that:
- Next.js middleware is server-side, not client-side.
- HttpOnly cookies are designed to be unreadable for security reasons.
- The backend should always be the authority on whether a session is valid.
They try to solve the problem in the wrong layer, leading to:
- Client-side hacks
- Token duplication
- Security regressions
- UI flicker from late redirects