# Clerk Nextjs Auth

> 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.

- Skill: `josiahsiegel/clerk-nextjs-auth` (Agent Skill)
- Install (CLI): `npx skillmds@latest add josiahsiegel/clerk-nextjs-auth`
- Raw SKILL.md: https://api.skillmd.com/api/skills/josiahsiegel/clerk-nextjs-auth/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: josiahsiegel (https://skillmd.com/u/josiahsiegel)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/josiahsiegel/clerk-nextjs-auth

---


# 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 `Link`s 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.
