Skip to main content
AI/MLjeremylongshore

clerk-prod-checklist

'Production readiness checklist for Clerk deployment.

Stars
2,267
Source
jeremylongshore/claude-code-plugins-plus-skills
Updated
2026-05-31
Slug
jeremylongshore--claude-code-plugins-plus-skills--clerk-prod-checklist
View on GitHubRaw SKILL.md

// install — copy + paste into any project

mkdir -p .claude/skills && curl -fsSL https://raw.githubusercontent.com/jeremylongshore/claude-code-plugins-plus-skills/HEAD/plugins/saas-packs/clerk-pack/skills/clerk-prod-checklist/SKILL.md -o .claude/skills/clerk-prod-checklist.md

Drops the SKILL.md into .claude/skills/clerk-prod-checklist.md. Works with Claude Code, Cursor, and any agent that loads SKILL.md files from .claude/skills/.

Clerk Production Checklist

Overview

Complete checklist to ensure your Clerk integration is production-ready. Covers environment config, security hardening, monitoring, error handling, and compliance.

Prerequisites

  • Clerk integration working in development
  • Production environment and domain configured
  • CI/CD pipeline ready

Instructions

Step 1: Environment Configuration Checklist

Check Status Action
Using pk_live_ keys [ ] Switch from test to live keys
CLERK_SECRET_KEY is sk_live_ [ ] Never use test keys in production
.env.local in .gitignore [ ] Prevent accidental secret commits
CLERK_WEBHOOK_SECRET set [ ] Required for webhook verification
Production domain in Clerk Dashboard [ ] Dashboard > Domains
Sign-in/sign-up URLs configured [ ] Set NEXT_PUBLIC_CLERK_SIGN_IN_URL etc.

Step 2: Validation Script

// scripts/prod-readiness.ts
import { createClerkClient } from '@clerk/backend'

async function validateProduction() {
  const checks: { name: string; pass: boolean; detail: string }[] = []

  // 1. Live keys check
  const pk = process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY || ''
  const sk = process.env.CLERK_SECRET_KEY || ''
  checks.push({
    name: 'Live publishable key',
    pass: pk.startsWith('pk_live_'),
    detail: pk.startsWith('pk_live_') ? 'Using live key' : `Using ${pk.slice(0, 8)}... (should be pk_live_)`,
  })
  checks.push({
    name: 'Live secret key',
    pass: sk.startsWith('sk_live_'),
    detail: sk.startsWith('sk_live_') ? 'Using live key' : 'Should be sk_live_ for production',
  })

  // 2. API connectivity
  try {
    const clerk = createClerkClient({ secretKey: sk })
    await clerk.users.getUserList({ limit: 1 })
    checks.push({ name: 'API connectivity', pass: true, detail: 'Backend API reachable' })
  } catch (err: any) {
    checks.push({ name: 'API connectivity', pass: false, detail: err.message })
  }

  // 3. Webhook secret
  checks.push({
    name: 'Webhook secret configured',
    pass: !!process.env.CLERK_WEBHOOK_SECRET,
    detail: process.env.CLERK_WEBHOOK_SECRET ? 'Set' : 'CLERK_WEBHOOK_SECRET missing',
  })

  // 4. Middleware exists
  const fs = await import('fs')
  const hasMiddleware = fs.existsSync('middleware.ts') || fs.existsSync('src/middleware.ts')
  checks.push({
    name: 'Middleware present',
    pass: hasMiddleware,
    detail: hasMiddleware ? 'Found' : 'middleware.ts not found at project root',
  })

  // Print results
  console.log('\n=== Clerk Production Readiness ===\n')
  for (const check of checks) {
    const icon = check.pass ? 'PASS' : 'FAIL'
    console.log(`[${icon}] ${check.name}: ${check.detail}`)
  }

  const allPass = checks.every((c) => c.pass)
  console.log(`\nResult: ${allPass ? 'READY for production' : 'NOT READY — fix failing checks'}`)
  process.exit(allPass ? 0 : 1)
}

validateProduction()

Run with:

npx tsx scripts/prod-readiness.ts

Step 3: Security Checklist

Check Status Action
Middleware protects all routes [ ] Verify non-public routes require auth
API routes check userId [ ] Return 401 if userId is null
Webhook signatures verified [ ] Use svix library for verification
CORS configured correctly [ ] Only allow production domain
Rate limiting on sensitive endpoints [ ] Use @upstash/ratelimit or similar
CSP headers set [ ] Add Clerk domains to Content-Security-Policy
No secret keys in client code [ ] CLERK_SECRET_KEY never exposed

Step 4: Monitoring Checklist

Check Status Action
Health check endpoint [ ] /api/health monitoring Clerk API
Error tracking (Sentry) [ ] Clerk user context in error reports
Auth event logging [ ] Log sign-in, sign-out, permission denied
Webhook monitoring [ ] Alert on failed webhook deliveries
Uptime monitoring [ ] External monitor hitting health endpoint

Step 5: Error Handling Checklist

Check Status Action
Custom error pages [ ] /not-found, /error pages handle auth errors
Graceful auth failures [ ] Redirect to sign-in, don't show stack traces
Webhook retry handling [ ] Idempotency keys prevent duplicate processing
Session expiry UX [ ] Show "session expired" prompt, not blank page
// app/error.tsx — global error boundary with auth context
'use client'
import { useAuth } from '@clerk/nextjs'

export default function Error({ error, reset }: { error: Error; reset: () => void }) {
  const { isSignedIn } = useAuth()

  return (
    <div>
      <h2>Something went wrong</h2>
      <p>{error.message}</p>
      <button onClick={reset}>Try again</button>
      {!isSignedIn && <a href="/sign-in">Sign in</a>}
    </div>
  )
}

Step 6: Performance Checklist

Check Status Action
Middleware matcher excludes static files [ ] Don't auth-check images, fonts, CSS
User data cached (React.cache()) [ ] Deduplicate within request
Auth components lazy loaded [ ] dynamic() for UserButton, SignInButton
Edge Runtime for middleware [ ] Faster cold starts on Vercel

Output

  • Environment configuration verified (live keys, webhook secret, domain)
  • Automated validation script (run in CI or before deploy)
  • Security, monitoring, error handling, and performance checklists
  • Global error boundary component with auth context

Error Handling

Error Cause Solution
Validation script fails Test keys in production Switch to pk_live_ / sk_live_ keys
API connectivity check fails Wrong secret key Verify key in Clerk Dashboard > API Keys
Middleware not found File in wrong location Place middleware.ts at project root (not inside app/)
Health check returns 503 Clerk API unreachable Check network, verify key, check status.clerk.com

Examples

CI Production Gate

# .github/workflows/deploy.yml — add as pre-deploy step
- name: Clerk production readiness
  run: npx tsx scripts/prod-readiness.ts
  env:
    NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: ${{ secrets.CLERK_PK_PROD }}
    CLERK_SECRET_KEY: ${{ secrets.CLERK_SK_PROD }}
    CLERK_WEBHOOK_SECRET: ${{ secrets.CLERK_WEBHOOK_SECRET_PROD }}

Resources

Next Steps

Proceed to clerk-upgrade-migration for SDK version upgrades.