Clerk: redirect loop after authentication [2026 fix]
Redirect URI mismatch or missing publishable key causes infinite loop. Fix: verify NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY and allowed redirect URLs in dashboard.
Clerk: Redirect Loop After Authentication [2026 Fix]
TL;DR
Cause: Your redirect URI in Clerk dashboard doesn't match your app's actual domain, orNEXT_PUBLIC_CLERK_PUBLISHABLE_KEY is missing/invalid.
Fix: Update Clerk dashboard redirect URLs to match your deployment domain AND verify your publishable key is set correctly in environment variables.---
Exact Console Error Messages
Watch for these in your browser console or server logs:
``` 1. "Clerk: Unable to complete sign in. Redirect URI mismatch."
2. "Error: NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY is not set or invalid"
3. "TypeError: Cannot read property 'redirectUrl' of undefined at AuthenticatedRoute"
4. "POST https://[your-clerk-domain].clerk.accounts.com/v1/verify failed - Invalid redirect_uri"
5. "Warning: useAuth() called outside of <ClerkProvider>. Make sure your app root wraps all routes." ```
---
Broken Code vs. Exact Fix
Problem 1: Missing or Incorrect Environment Variable
BROKEN: ```javascript // .env.local (missing or wrong value) CLERK_SECRET_KEY=sk_test_xxx // Missing NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY entirely
// app.tsx import { ClerkProvider } from '@clerk/nextjs';
export default function App() { return ( <ClerkProvider> <YourApp /> </ClerkProvider> ); } ```
FIXED: ```javascript // .env.local (verified and complete) NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_abc123xyz... CLERK_SECRET_KEY=sk_live_abc123xyz...
// app.tsx (same structure works now because env is correct) import { ClerkProvider } from '@clerk/nextjs';
export default function App() { return ( <ClerkProvider publishableKey={process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY} > <YourApp /> </ClerkProvider> ); } ```
Problem 2: Redirect URL Mismatch in Dashboard
BROKEN: ```javascript // Your app deployed to: https://myapp.vercel.app // But Clerk dashboard configured with: http://localhost:3000 // Result: After sign-in, user sent to localhost (not your deployed domain) // Browser can't reach it → app refreshes → repeats auth → infinite loop ```
FIXED: ```javascript // Clerk Dashboard → Applications → [Your App] → Allowed redirect URLs // Add EXACTLY these (no trailing slashes): // Development: http://localhost:3000 // Production: https://myapp.vercel.app // Staging: https://staging-myapp.vercel.app
// Your code doesn't change, but the redirect now succeeds: import { useRouter } from 'next/navigation'; import { useAuth } from '@clerk/nextjs';
export default function Dashboard() { const router = useRouter(); const { isSignedIn } = useAuth();
if (isSignedIn) { // This will now redirect to correct domain router.push('/dashboard'); } return <div>Loading...</div>; } ```
Problem 3: ClerkProvider Wrapping Issue
BROKEN: ```javascript // pages/index.tsx (app router) import { useAuth } from '@clerk/nextjs';
export default function Home() { const { isSignedIn } = useAuth(); // ERROR: Called outside provider return isSignedIn ? <Dashboard /> : <SignIn />; }
// Missing provider wrapper at root level ```
FIXED: ```javascript // app/layout.tsx (app router) import { ClerkProvider } from '@clerk/nextjs';
export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <ClerkProvider> <html> <body>{children}</body> </html> </ClerkProvider> ); }
// app/page.tsx (now safe to use useAuth) import { useAuth } from '@clerk/nextjs';
export default function Home() { const { isSignedIn } = useAuth(); return isSignedIn ? <Dashboard /> : <SignIn />; } ```
---
Version-Specific Behavior
⚠️ EXPLICIT UNCERTAINTY: Clerk's redirect behavior changed between v4 and v5. If you're on v4 and upgraded without checking migration docs, redirect URL handling differs. Run npm list @clerk/nextjs to verify your version. This guide assumes v5 (current as of 2026).
---
Still Broken? Check These Too
1. CORS/Mixed Content: If development is http://localhost:3000 but you're testing production https://myapp.com, browser blocks it. Always use matching protocols (both http OR both https).
2. Vercel Environment Variables Not Synced: You updated .env.local but deployed without redeploying. Solution: redeploy your app after updating env vars, or manually set them in Vercel dashboard → Settings → Environment Variables.
3. Incorrect Clerk Instance: You have multiple Clerk projects (dev, staging, prod). Your code uses publishable key from wrong project. Check dashboard URL matches your app's intended environment.
---
Quick Debug Checklist
echo $NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY returns a value (starts with pk_)layout.tsx or _app.tsx---
Related Guides
[Clerk setup for Next.js 15](/?guide=clerk-nextjs-setup) | [Authentication redirect patterns](/?guide=auth-redirects)
---
Official Documentation
[Clerk Redirect URLs Configuration](https://clerk.com/docs/deployments/overview) [Clerk Environment Variables](https://clerk.com/docs/deployments/clerk-environment-variables)
---
Found a different variation? Drop it in the comments.