Clerk Next.js Auth
Overview
Use this skill for Clerk integrations in Next.js projects, especially App Router projects using @clerk/nextjs. Focus on correct file placement, protected-route coverage, server/client boundaries, and modern Clerk middleware behavior.
Quick Reference
| Need |
Preferred Clerk primitive |
| Wrap app with auth context and prebuilt components |
ClerkProvider |
| Read auth state in App Router server code |
auth() from @clerk/nextjs/server |
| Read current user object in App Router server code |
currentUser() from @clerk/nextjs/server |
| Protect routes before rendering |
clerkMiddleware() + auth.protect() |
| Group protected/public routes |
createRouteMatcher() |
| Pages Router API auth |
getAuth(req) |
| Administrative Backend API calls |
clerkClient from server-only code |
| Client UI state |
useUser(), useAuth(), useClerk() |
Setup Pattern
Install the official package and wire environment variables from the Clerk Dashboard. Clerk recommends CLI setup with npx clerk@latest init --framework next when scaffolding from scratch, but always verify the resulting files against the project's Next.js version and repo conventions.
Place ClerkProvider in the root layout for App Router apps. Keep it near the top of the React tree so prebuilt components and hooks can access Clerk context. Use client hooks only inside client components.
Use proxy.ts at the project root or src/proxy.ts for current Next.js projects. Use middleware.ts for Next.js 15 and older projects that still require middleware naming. The implementation is otherwise the same. Do not blindly create both files.
Middleware and Route Protection
Start with the rule that surprises most teams: clerkMiddleware() does not protect any route by default. Add explicit protection logic.
Use createRouteMatcher() to declare route groups. Prefer positive matchers for high-risk surfaces, such as dashboard pages and mutating APIs. Use an inverted public-route matcher only after confirming every public route is listed.
Common matcher coverage:
- Skip Next.js internals and common static files.
- Always include API/TRPC surfaces when API auth is required, such as
/(api|trpc)(.*).
- Include Clerk frontend API proxy paths such as
/__clerk/(.*) when using Clerk proxying.
- Include any custom Clerk proxy path.
- Include route groups that look public but call Server Actions or Route Handlers with private side effects.
Use await auth.protect() when default Clerk behavior is acceptable. Use const { isAuthenticated, redirectToSignIn } = await auth() when custom JSON responses, logging, or redirects are required.
Auth and Authorization Behavior
auth() is App Router server-only and requires clerkMiddleware(). Use it in Server Components, Route Handlers, and Server Actions. It returns the request's Auth object and redirectToSignIn().
auth.protect() can enforce:
- Signed-in access.
- Roles, permissions, and custom
has() authorization logic.
- Feature and plan checks where enabled.
- Accepted token types such as
session_token, oauth_token, api_key, m2m_token, or any.
Failure behavior matters in tests:
- Authenticated and authorized returns auth state.
- Authenticated but unauthorized returns
404 by default.
- Unauthenticated document requests redirect to sign-in.
- Unauthenticated non-document/API requests with session token defaults can return
404.
- Unauthenticated machine-token requests can return
401.
Use unauthenticatedUrl and unauthorizedUrl only when the resulting behavior is intentional for both browser navigation and API callers.
App Router Server APIs
Use auth() when only IDs, session state, active organization, token retrieval, or authorization checks are needed. Use currentUser() only when the full user object is needed because it can add Backend API work.
For token retrieval, use getToken() from the Auth object and always await it. Use a custom JWT template only when an external service requires it; otherwise prefer the default session token or server-side Clerk auth state.
In Route Handlers, call auth() or auth.protect() at the top. Authenticate and authorize before input parsing that can trigger costly work or side effects. For organization-scoped resources, compare the active organization ID to the resource's organization owner before mutating.
In Server Actions, call auth() or auth.protect() inside the action, not only in the page that renders the form. Server Actions can be invoked independently from the original UI state.
When a page needs auth-only rendering, protect at the page/layout level or in middleware. Use middleware when the app should avoid rendering work for signed-out users.
Server/Client Boundary Rules
Keep these boundaries strict:
- Use
auth(), currentUser(), clerkClient, Backend API operations, and secret keys only on the server.
- Use
useUser(), useAuth(), useSignIn(), useSignUp(), and prebuilt UI components in client components.
- Treat client hooks as UI state, not final authorization.
- Avoid passing full user records into client components unless every field is intentionally exposed.
- Keep
CLERK_SECRET_KEY, CLERK_WEBHOOK_SIGNING_SECRET, and CLERK_ENCRYPTION_KEY server-only. Client code should use publishable keys only.
Environment Variables
Common Next.js Clerk variables:
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: browser-safe publishable key.
CLERK_SECRET_KEY: server-only Backend API credential.
NEXT_PUBLIC_CLERK_SIGN_IN_URL, NEXT_PUBLIC_CLERK_SIGN_UP_URL: app route paths for auth UI.
NEXT_PUBLIC_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL, NEXT_PUBLIC_CLERK_SIGN_UP_FALLBACK_REDIRECT_URL: post-auth fallback destinations.
NEXT_PUBLIC_CLERK_SIGN_IN_FORCE_REDIRECT_URL, NEXT_PUBLIC_CLERK_SIGN_UP_FORCE_REDIRECT_URL: force destinations when product flow requires it.
CLERK_WEBHOOK_SIGNING_SECRET: endpoint-specific webhook secret for API route handlers.
CLERK_ENCRYPTION_KEY: required when middleware options include a secretKey.
Production keys use pk_live_ and sk_live_; development keys use pk_test_ and sk_test_. Never mix them.
Organizations and Permissions
Use Clerk organization context deliberately. Check the active organization ID when protecting organization resources. For authorization, prefer explicit role/permission checks that match Clerk's semantics.
Important nuance: server-side has() works for Custom Permissions. System Permissions are not available in session token claims, so check roles where Clerk requires it. Do not assume every Dashboard permission string is valid in every server-side check.
Default-deny unknown roles, missing active organization, pending sessions, and absent permissions. Avoid fallback logic that treats “no organization selected” as organization access unless that is an intentional product rule.
Custom Domains, Proxying, and CSP
When using a custom Clerk Frontend API domain or proxy:
- Ensure the middleware matcher includes
/__clerk/(.*) or the configured custom proxy path.
- In App Router, a literal
__clerk route folder must be named %5F%5Fclerk.
- If using route handlers for proxying, do not assume
proxyUrl is automatically derived; configure it as Clerk documents for the selected approach.
- Test production host headers and forwarded proto/host headers, not just localhost.
- If the app uses CSP, add Clerk script/connect/frame/img/style directives according to Clerk's current CSP guide and the SDK components used.
Production Gotchas
- Add
prefetch={false} on public-page Links pointing to protected pages if prefetching causes failed auth requests or noisy console errors.
- Enable Clerk middleware
{ debug: true } only in development or temporary diagnostics.
- For custom domains, proxy URLs, and multi-tenant dynamic keys, test in an environment that matches production host headers.
- Dynamic keys such as
signUpUrl, signInUrl, secretKey, and publishableKey supplied through middleware are not client-accessible through middleware.
- If passing
secretKey through middleware options, configure CLERK_ENCRYPTION_KEY as Clerk requires.
- Do not use a dev Clerk instance for production preview of OAuth, DNS, custom domains, or webhooks when behavior depends on production instance settings.
Troubleshooting Matrix
| Symptom |
Likely check |
| Protected route is public |
clerkMiddleware() present but no auth.protect() or route matcher |
API returns 404 instead of 401 |
Clerk authorization failure behavior or session-token API failure semantics |
auth() unavailable or empty |
Missing clerkMiddleware()/proxy, wrong file name for Next version, matcher excluded route |
| Client hook crashes |
Component is not wrapped in ClerkProvider or hook runs before loaded state |
| Production sign-in loops |
Mixed keys, redirect URL mismatch, missing forwarded headers, domain/DNS misconfiguration |
| Protected link logs fetch errors |
Next Link prefetch to protected route; use prefetch={false} |
| System permission check fails |
Server-side has() cannot see system permission claims; check roles |
Validation Checklist
- Confirm every protected page, layout, Server Action, Route Handler, API route, and tRPC route is covered by middleware or in-route guard.
- Test signed-out, signed-in unauthorized, signed-in authorized, wrong organization, pending session, and wrong token type.
- Test direct API calls, not only browser navigation.
- Verify production environment variables, DNS, domain, OAuth, webhook, and redirect settings.
- Confirm no server-only imports appear in client components.
- Confirm no secret keys or webhook signing secrets appear in bundled client code or public environment variables.
- Inspect production request headers for
Authorization, Host, Origin, Referer, User-Agent, X-Forwarded-Host, and X-Forwarded-Proto/CloudFront-Forwarded-Proto when auth state differs from local.
1---2name: clerk-nextjs-auth3description: Use for Clerk in Next.js. PROACTIVELY activate for @clerk/nextjs setup, App Router auth()/currentUser(), clerkMiddleware(), proxy.ts/middleware.ts, createRouteMatcher(), protected pages/layouts/Route Handlers/Server Actions/API routes/tRPC, auth.protect() role/permission/token checks, ClerkProvider placement, server-only clerkClient, Link prefetch, redirects, 401/404 auth failures, custom domains, __clerk proxy paths, and deployment gotchas. Provides file patterns, server/client boundary rules, matcher templates, and production checks.4---5
6# Clerk Next.js Auth
7
8## Overview
9
10Use this skill for Clerk integrations in Next.js projects, especially App Router projects using `@clerk/nextjs`. Focus on correct file placement, protected-route coverage, server/client boundaries, and modern Clerk middleware behavior.
11
12## Quick Reference
13
14| Need | Preferred Clerk primitive |
15|---|---|
16| Wrap app with auth context and prebuilt components | `ClerkProvider` |
17| Read auth state in App Router server code | `auth()` from `@clerk/nextjs/server` |
18| Read current user object in App Router server code | `currentUser()` from `@clerk/nextjs/server` |
19| Protect routes before rendering | `clerkMiddleware()` + `auth.protect()` |
20| Group protected/public routes | `createRouteMatcher()` |
21| Pages Router API auth | `getAuth(req)` |
22| Administrative Backend API calls | `clerkClient` from server-only code |
23| Client UI state | `useUser()`, `useAuth()`, `useClerk()` |
24
25## Setup Pattern
26
27Install the official package and wire environment variables from the Clerk Dashboard. Clerk recommends CLI setup with `npx clerk@latest init --framework next` when scaffolding from scratch, but always verify the resulting files against the project's Next.js version and repo conventions.
28
29Place `ClerkProvider` in the root layout for App Router apps. Keep it near the top of the React tree so prebuilt components and hooks can access Clerk context. Use client hooks only inside client components.
30
31Use `proxy.ts` at the project root or `src/proxy.ts` for current Next.js projects. Use `middleware.ts` for Next.js 15 and older projects that still require middleware naming. The implementation is otherwise the same. Do not blindly create both files.
32
33## Middleware and Route Protection
34
35Start with the rule that surprises most teams: `clerkMiddleware()` does not protect any route by default. Add explicit protection logic.
36
37Use `createRouteMatcher()` to declare route groups. Prefer positive matchers for high-risk surfaces, such as dashboard pages and mutating APIs. Use an inverted public-route matcher only after confirming every public route is listed.
38
39Common matcher coverage:
40
41- Skip Next.js internals and common static files.
42- Always include API/TRPC surfaces when API auth is required, such as `/(api|trpc)(.*)`.
43- Include Clerk frontend API proxy paths such as `/__clerk/(.*)` when using Clerk proxying.
44- Include any custom Clerk proxy path.
45- Include route groups that look public but call Server Actions or Route Handlers with private side effects.
46
47Use `await auth.protect()` when default Clerk behavior is acceptable. Use `const { isAuthenticated, redirectToSignIn } = await auth()` when custom JSON responses, logging, or redirects are required.
48
49## Auth and Authorization Behavior
50
51`auth()` is App Router server-only and requires `clerkMiddleware()`. Use it in Server Components, Route Handlers, and Server Actions. It returns the request's Auth object and `redirectToSignIn()`.
52
53`auth.protect()` can enforce:
54
55- Signed-in access.
56- Roles, permissions, and custom `has()` authorization logic.
57- Feature and plan checks where enabled.
58- Accepted token types such as `session_token`, `oauth_token`, `api_key`, `m2m_token`, or `any`.
59
60Failure behavior matters in tests:
61
62- Authenticated and authorized returns auth state.
63- Authenticated but unauthorized returns `404` by default.
64- Unauthenticated document requests redirect to sign-in.
65- Unauthenticated non-document/API requests with session token defaults can return `404`.
66- Unauthenticated machine-token requests can return `401`.
67
68Use `unauthenticatedUrl` and `unauthorizedUrl` only when the resulting behavior is intentional for both browser navigation and API callers.
69
70## App Router Server APIs
71
72Use `auth()` when only IDs, session state, active organization, token retrieval, or authorization checks are needed. Use `currentUser()` only when the full user object is needed because it can add Backend API work.
73
74For token retrieval, use `getToken()` from the Auth object and always await it. Use a custom JWT template only when an external service requires it; otherwise prefer the default session token or server-side Clerk auth state.
75
76In Route Handlers, call `auth()` or `auth.protect()` at the top. Authenticate and authorize before input parsing that can trigger costly work or side effects. For organization-scoped resources, compare the active organization ID to the resource's organization owner before mutating.
77
78In Server Actions, call `auth()` or `auth.protect()` inside the action, not only in the page that renders the form. Server Actions can be invoked independently from the original UI state.
79
80When a page needs auth-only rendering, protect at the page/layout level or in middleware. Use middleware when the app should avoid rendering work for signed-out users.
81
82## Server/Client Boundary Rules
83
84Keep these boundaries strict:
85
86- Use `auth()`, `currentUser()`, `clerkClient`, Backend API operations, and secret keys only on the server.
87- Use `useUser()`, `useAuth()`, `useSignIn()`, `useSignUp()`, and prebuilt UI components in client components.
88- Treat client hooks as UI state, not final authorization.
89- Avoid passing full user records into client components unless every field is intentionally exposed.
90- Keep `CLERK_SECRET_KEY`, `CLERK_WEBHOOK_SIGNING_SECRET`, and `CLERK_ENCRYPTION_KEY` server-only. Client code should use publishable keys only.
91
92## Environment Variables
93
94Common Next.js Clerk variables:
95
96- `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY`: browser-safe publishable key.
97- `CLERK_SECRET_KEY`: server-only Backend API credential.
98- `NEXT_PUBLIC_CLERK_SIGN_IN_URL`, `NEXT_PUBLIC_CLERK_SIGN_UP_URL`: app route paths for auth UI.
99- `NEXT_PUBLIC_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL`, `NEXT_PUBLIC_CLERK_SIGN_UP_FALLBACK_REDIRECT_URL`: post-auth fallback destinations.
100- `NEXT_PUBLIC_CLERK_SIGN_IN_FORCE_REDIRECT_URL`, `NEXT_PUBLIC_CLERK_SIGN_UP_FORCE_REDIRECT_URL`: force destinations when product flow requires it.
101- `CLERK_WEBHOOK_SIGNING_SECRET`: endpoint-specific webhook secret for API route handlers.
102- `CLERK_ENCRYPTION_KEY`: required when middleware options include a `secretKey`.
103
104Production keys use `pk_live_` and `sk_live_`; development keys use `pk_test_` and `sk_test_`. Never mix them.
105
106## Organizations and Permissions
107
108Use Clerk organization context deliberately. Check the active organization ID when protecting organization resources. For authorization, prefer explicit role/permission checks that match Clerk's semantics.
109
110Important nuance: server-side `has()` works for Custom Permissions. System Permissions are not available in session token claims, so check roles where Clerk requires it. Do not assume every Dashboard permission string is valid in every server-side check.
111
112Default-deny unknown roles, missing active organization, pending sessions, and absent permissions. Avoid fallback logic that treats “no organization selected” as organization access unless that is an intentional product rule.
113
114## Custom Domains, Proxying, and CSP
115
116When using a custom Clerk Frontend API domain or proxy:
117
118- Ensure the middleware matcher includes `/__clerk/(.*)` or the configured custom proxy path.
119- In App Router, a literal `__clerk` route folder must be named `%5F%5Fclerk`.
120- If using route handlers for proxying, do not assume `proxyUrl` is automatically derived; configure it as Clerk documents for the selected approach.
121- Test production host headers and forwarded proto/host headers, not just localhost.
122- If the app uses CSP, add Clerk script/connect/frame/img/style directives according to Clerk's current CSP guide and the SDK components used.
123
124## Production Gotchas
125
126- Add `prefetch={false}` on public-page `Link`s pointing to protected pages if prefetching causes failed auth requests or noisy console errors.
127- Enable Clerk middleware `{ debug: true }` only in development or temporary diagnostics.
128- For custom domains, proxy URLs, and multi-tenant dynamic keys, test in an environment that matches production host headers.
129- Dynamic keys such as `signUpUrl`, `signInUrl`, `secretKey`, and `publishableKey` supplied through middleware are not client-accessible through middleware.
130- If passing `secretKey` through middleware options, configure `CLERK_ENCRYPTION_KEY` as Clerk requires.
131- Do not use a dev Clerk instance for production preview of OAuth, DNS, custom domains, or webhooks when behavior depends on production instance settings.
132
133## Troubleshooting Matrix
134
135| Symptom | Likely check |
136|---|---|
137| Protected route is public | `clerkMiddleware()` present but no `auth.protect()` or route matcher |
138| API returns `404` instead of `401` | Clerk authorization failure behavior or session-token API failure semantics |
139| `auth()` unavailable or empty | Missing `clerkMiddleware()`/proxy, wrong file name for Next version, matcher excluded route |
140| Client hook crashes | Component is not wrapped in `ClerkProvider` or hook runs before loaded state |
141| Production sign-in loops | Mixed keys, redirect URL mismatch, missing forwarded headers, domain/DNS misconfiguration |
142| Protected link logs fetch errors | Next `Link` prefetch to protected route; use `prefetch={false}` |
143| System permission check fails | Server-side `has()` cannot see system permission claims; check roles |
144
145## Validation Checklist
146
147- Confirm every protected page, layout, Server Action, Route Handler, API route, and tRPC route is covered by middleware or in-route guard.
148- Test signed-out, signed-in unauthorized, signed-in authorized, wrong organization, pending session, and wrong token type.
149- Test direct API calls, not only browser navigation.
150- Verify production environment variables, DNS, domain, OAuth, webhook, and redirect settings.
151- Confirm no server-only imports appear in client components.
152- Confirm no secret keys or webhook signing secrets appear in bundled client code or public environment variables.
153- Inspect production request headers for `Authorization`, `Host`, `Origin`, `Referer`, `User-Agent`, `X-Forwarded-Host`, and `X-Forwarded-Proto`/`CloudFront-Forwarded-Proto` when auth state differs from local.