Role-based route protection in Next.js can be implemented at three levels — Middleware, Layout, and Page. Each has different capabilities, trade-offs, and ideal use cases. Middleware runs earliest at the Edge before any rendering, Layout protects entire route segments with server-side data access, and Page gives the most granular per-page control. The best production apps combine all three layers as a defense-in-depth strategy.
Think of role-based protection as layers of security. Middleware is the bouncer at the front door — fast, early, but limited in what it can verify. Layout is the security desk inside — has access to your full database and session. Page is the room itself — most granular, knows exactly what content to show or hide per role. Using only one layer is fragile. Using all three together creates a robust, secure, and performant protection system.
Middleware — runs at Edge before rendering, best for fast redirects based on token/cookie presence
Layout — runs on server with full DB access, best for role verification across route segments
Page — runs on server or client, best for granular per-page role checks and UI-level protection
Middleware — runs at Edge before rendering, fastest, no DB access, best for token/cookie presence checks
Middleware — can redirect before any HTML is generated, lowest latency protection
Middleware — cannot verify role from DB, only from JWT claims (which can be stale)
Layout — runs on server with full DB and session access, protects entire route segments
Layout — re-runs only when segment mounts, not on every child navigation
Layout — best for segment-level protection where all child routes need same role
Page — most granular, per-page permission checks with specific data scoping
Page — can use notFound() to hide existence of a page entirely from unauthorized users
Page — UI-level gating to show/hide buttons and sections based on permissions
Never rely on client-side role checks alone — always back up with server-side verification
Never trust JWT role claims without DB verification for sensitive operations — roles can change
Use notFound() instead of redirect('/forbidden') to hide existence of sensitive pages
Always scope data queries to the authenticated user — never return data outside their org/tenant
Middleware alone is not enough — JWT can be stale if role changed after token was issued
Defense in depth — always use at least two layers (middleware + layout or middleware + page)
Log all unauthorized access attempts for security auditing