Complete checklist to ensure your Clerk integration is production-ready.
pk_test_, sk_test_) to live keys (pk_live_, sk_live_)# Verify production keys
echo "Publishable key starts with: ${NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY:0:8}"
# Should output: pk_live_
# Required production variables
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_...
CLERK_SECRET_KEY=sk_live_...
CLERK_WEBHOOK_SECRET=whsec_...
# Optional but recommended
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/dashboard
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/onboarding
// middleware.ts - Production configuration
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
const isPublicRoute = createRouteMatcher([
'/',
'/sign-in(.*)',
'/sign-up(.*)',
'/api/webhooks(.*)',
'/api/public(.*)'
])
export default clerkMiddleware(async (auth, request) => {
if (!isPublicRoute(request)) {
await auth.protect()
}
})
user.created
user.updated
user.deleted
session.created
session.revoked
organization.created (if using orgs)// Verify webhook endpoint is accessible
// POST https://yourdomain.com/api/webhooks/clerk
// app/error.tsx
'use client'
export default function Error({ error, reset }: {
error: Error
reset: () => void
}) {
return (
<div>
<h2>Authentication Error</h2>
<p>{error.message}</p>
<button onClick={reset}>Try again</button>
</div>
)
}
// Optimized middleware matcher
export const config = {
matcher: [
'/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
'/(api|trpc)(.*)'
]
}
// Example: Sentry integration
import * as Sentry from '@sentry/nextjs'
export async function POST(request: Request) {
try {
// ... auth logic
} catch (error) {
Sentry.captureException(error, {
tags: { component: 'clerk-auth' }
})
throw error
}
}
// Example: Playwright test
test('user can sign in', async ({ page }) => {
await page.goto('/sign-in')
await page.fill('input[name="email"]', 'test@example.com')
await page.fill('input[name="password"]', 'password123')
await page.click('button[type="submit"]')
await expect(page).toHaveURL('/dashboard')
})
#!/bin/bash
# scripts/validate-production.sh
echo "=== Clerk Production Validation ==="
# Check environment
if [[ $NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY != pk_live_* ]]; then
echo "ERROR: Not using production publishable key"
exit 1
fi
if [[ -z "$CLERK_SECRET_KEY" ]]; then
echo "ERROR: CLERK_SECRET_KEY not set"
exit 1
fi
if [[ -z "$CLERK_WEBHOOK_SECRET" ]]; then
echo "WARNING: CLERK_WEBHOOK_SECRET not set"
fi
# Check middleware exists
if [[ ! -f "middleware.ts" ]]; then
echo "WARNING: middleware.ts not found"
fi
echo "=== Validation Complete ==="
Proceed to clerk-upgrade-migration for SDK version upgrades.