Protect content from unauthenticated users - Access control - Next.js | Clerk Docs
Protect content from unauthenticated users
This guide demonstrates how to protect pages, Route Handlers, and Server Actions from unauthenticated users in your Next.js application.
To protect your application, every resource that reads or mutates protected data needs its own check. In a Next.js app, this means protecting:
- Server Components: Layouts and pages
- Route Handlers:Your internal Next.js API endpoints
- Server Actions: These run as API endpoints, so protect them like any other handler.
- External API endpoints your app calls: These endpoints must enforce their own authentication.
Tip
To check whether a user is authorized (has a specific Role, Permission, Feature, or Plan) in addition to being signed in, see Authorization checks. This guide focuses on authentication (whether a user is signed in).
Tip
To help verify that protected Next.js resources keep their own auth checks over time, see the @clerk/eslint-plugin reference.
Server-side
auth() is an App Router-specific helper that you can use inside of your Route Handlers, Server Components, and Server Actions.
Use auth.protect() to:
Redirect unauthenticated users to the sign-in route for document requests.
- Return a
404error for authenticated users who are not authorized to access the route or content.
- Return a
Use auth() and check
isAuthenticatedyourself if you want more control over the response.
The following examples demonstrate both methods.
Protect a page
import { auth } from '@clerk/nextjs/server'
export default async function Page() {
// Redirects to the sign-in route if the user is not signed in
await auth.protect()
return <h1>Hello world</h1>
}
Protect a layout
You can run an auth check in a layout the same way as in a page, and you should if the layout itself reads privileged data (for example, when prefetching). But a layout check alone isn't enough: layouts don't re-render on client-side navigation, so the session isn't re-checked on every route change. Protect each page (and every other resource that reads or mutates data) directly, and treat a layout check as an addition, not a replacement. For more information, see the Next.js docs.
Protect a Route Handler
For non-document requests, such as API requests, auth.protect() does not redirect the user to the sign-in page. Instead, it returns:
- A
404error for unauthenticated requests with session token type. - A
401error for unauthenticated requests with machine token types.
import { auth } from '@clerk/nextjs/server'
export const GET = async () => {
// Returns a `404` error for unauthenticated requests
const { userId } = await auth.protect()
return Response.json({ userId })
}
Protect a Server Action
Server Actions can be called from any Client Component, so verify the user inside the action itself, before running any logic that depends on them being signed in.
Inside a Server Action, auth.protect() doesn't redirect. Because a Server Action isn't a document request, it rejects unauthenticated requests with a 401 error instead. To handle the unauthenticated case yourself, use auth().
'use server'
import { auth } from '@clerk/nextjs/server'
export async function createPost(formData: FormData) {
// Returns a 401 error if the user is not signed in
const { userId } = await auth.protect()
// Add your Server Action logic using the `userId`
}
Client-side
On the client, you can read the isSignedIn property from the useAuth() or useUser() hook to conditionally render UI based on whether the user is signed in.
Warning
A client-side check is a UX affordance, not a security boundary. By the time this code runs, the component has already been sent to the browser, so anything it contains is available to the user regardless of the check.
Protect the data instead: guard the Route Handlers, Server Actions, and external API endpoints the client calls. Protecting the Server Components that render these Client Components also guards any data you pass down as props.
'use client'
import { useAuth } from '@clerk/nextjs'
export default function Page() {
const { isLoaded, isSignedIn } = useAuth()
// Handle loading state
if (!isLoaded) return <h1>Loading...</h1>
if (!isSignedIn) {
// Add logic to handle the unauthenticated user
// This example renders a UI but you could also redirect to the sign-in page instead
return <h1>You must be signed in to view this page</h1>
}
return <h1>Hello world</h1>
}