Protecting a route means ensuring only authenticated (and sometimes authorized) users can view it. This lesson covers the layered approach: middleware for fast redirects, and an authoritative check inside the route itself.
Guarding a Route with a Layout Check
The most direct way to protect a section of your app is checking authentication status inside a shared layout.tsx for that section — since every nested route renders inside it, the check applies automatically to the whole section without repeating it in every page.
If the check fails, calling redirect("/login") immediately stops rendering and sends the user to the login page — this is the authoritative check that should always be present, regardless of what middleware also does.
// app/dashboard/layout.tsx
import { redirect } from "next/navigation";
import { auth } from "@/auth";
export default async function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
const session = await auth();
if (!session) {
redirect("/login");
}
return <div>{children}</div>;
}
Every route nested under /dashboard is now guarded by this single check, with no per-page repetition needed.
Layered Protection Pattern
1. middleware.ts — fast, cookie-presence redirect (UX layer)
2. layout.tsx — authoritative session check for a whole section
3. page.tsx — additional per-resource authorization if needed
(e.g. "is this specifically YOUR order?")
Middleware alone is not sufficient — it should be a fast UX layer, not your only security boundary.
A layout-level check protects every nested route beneath it with one piece of code.
Resource-specific authorization ("can this user access this specific record?") still needs its own check.
redirect() inside a Server Component immediately halts rendering and navigates the browser.
Protected Routes Cheat Sheet
Where each type of check typically belongs.
Check
Typical Location
"Is there a session cookie at all?"
middleware.ts
"Is this session actually valid?"
Layout or page (auth() / session verification)
"Does this user have the right role?"
Layout, page, or a dedicated authorization helper
"Does this user own this specific resource?"
The specific page/Server Action handling that resource
Role-Based Access Control
Beyond "logged in or not", many apps need role checks — an admin section that regular users shouldn't access even while authenticated. This is typically an additional check layered on top of the authentication check, comparing the session's role claim against what the route requires.
export default async function AdminLayout({ children }: { children: React.ReactNode }) {
const session = await auth();
if (!session) redirect("/login");
if (session.user.role !== "admin") redirect("/unauthorized");
return <div>{children}</div>;
}
Protecting Server Actions Too
Route-level protection guards what a user can see, but doesn't automatically protect Server Actions — someone could still attempt to call a mutation directly. Every sensitive Server Action needs its own explicit authentication/authorization check at the top of the function.
Common Mistakes
Protecting a route's UI but forgetting to protect the Server Actions it uses for mutations.
Relying solely on middleware, without an authoritative check inside the actual route.
Checking authentication but not authorization (role/ownership) where it's actually required.
Redirecting to a login page without preserving the originally requested URL for post-login navigation.
Key Takeaways
Layout-level checks efficiently protect every nested route in a section with one piece of code.
Middleware should complement, not replace, an authoritative check inside the route itself.
Role-based access control is an additional check layered on top of basic authentication.
Server Actions require their own explicit protection — route protection doesn't cover them automatically.
redirect() immediately halts rendering, making it the standard way to enforce access control server-side.
Pro Tip
When redirecting an unauthenticated user to /login, pass the originally requested path as a query parameter (e.g. /login?from=/dashboard/settings) so you can redirect them back to where they were headed immediately after a successful login — this small detail meaningfully improves the login experience.
You now understand protecting routes. Next, learn about session management — how sessions are created, stored, and expired over time.