Clerk: redirect loop after authentication [2026 fix]
Clerk redirect loop: misconfigured callback URL or stale session state. Fix: verify CLERK_REDIRECT_URL matches dashboard + clear browser cache.
Clerk: Redirect Loop After Authentication [2026 Fix]
TL;DR
Cause: YourCLERK_REDIRECT_URL environment variable doesn't match the Allowed Redirect URLs in your Clerk dashboard, or your browser has stale authentication state.Fix: Update CLERK_REDIRECT_URL to match dashboard settings AND clear browser localStorage/cookies for your domain.
---
Console Error Messages You'll See
Here are the exact error signatures from production:
``` 1. "Redirecting to /sign-in after successful sign-up (infinite loop detected)" at ClerkProvider.redirectAfterSignUp (clerk.browser.js:1243)
2. "GET /api/auth/callback?code=clerk_live_... 307 Temporary Redirect -> GET /sign-in 200 OK -> GET /api/auth/callback... (loop)"
3. "[Clerk] Invalid redirect URL: http://localhost:3000/callback does not match any Allowed Redirect URL in dashboard"
4. "Sign in successful but redirect_url is undefined. Falling back to default route."
5. "localStorage contains stale session_id. useUser() hook returned null despite successful auth." ```
---
Broken Code → Fixed Code
Problem 1: Environment Variable Mismatch
BROKEN (.env.local): ```javascript // .env.local CLERK_REDIRECT_URL=http://localhost:3000/dashboard CLERK_PUBLISHABLE_KEY=pk_test_... CLERK_SECRET_KEY=sk_test_... ```
But in Clerk Dashboard → Application → Settings → Allowed Redirect URLs you have: ``` http://localhost:3000 http://localhost:3000/auth/callback ```
Result: After sign-in, Clerk redirects to /dashboard → not recognized → redirects to /sign-in → loop.
FIXED (.env.local): ```javascript // .env.local // Match EXACTLY what's in Clerk Dashboard CLERK_REDIRECT_URL=http://localhost:3000 CLERK_PUBLISHABLE_KEY=pk_test_... CLERK_SECRET_KEY=sk_test_... ```
Clerk Dashboard → Application → Settings → Allowed Redirect URLs: ``` http://localhost:3000 https://yourdomain.com https://yourdomain.com/dashboard ```
---
Problem 2: Stale Session State in Browser
BROKEN (your middleware/route handler after redirect): ```javascript // app/api/auth/callback/route.js import { handleCallback } from '@clerk/nextjs'
export const GET = handleCallback() // Problem: Browser localStorage still has old session_id // useUser() hook returns null, triggers redirect to /sign-in ```
FIXED (clear + reinitialize): ```javascript // app/api/auth/callback/route.js import { handleCallback } from '@clerk/nextjs'
export const GET = async (req, res) => { // Clear stale session before handling new callback try { const response = await handleCallback()(req, res) // Force cache invalidation res.setHeader('Cache-Control', 'no-store, must-revalidate') res.setHeader('Pragma', 'no-cache') return response } catch (err) { console.error('[Clerk] Callback error:', err.message) throw err } } ```
For React client-side (clear localStorage): ```javascript // Hook to run after Clerk loads import { useEffect } from 'react' import { useAuth } from '@clerk/nextjs'
export function AuthDebugger() { const { isSignedIn } = useAuth()
useEffect(() => { if (typeof window !== 'undefined') { // Clear corrupted session data const sessionId = localStorage.getItem('clerk.sessionId') if (sessionId && !isSignedIn) { console.warn('[Clerk] Clearing stale session:', sessionId) localStorage.removeItem('clerk.sessionId') localStorage.removeItem('clerk.userId') // Hard refresh to re-authenticate window.location.href = '/' } } }, [isSignedIn])
return null } ```
---
Problem 3: Production vs. Development Mismatch
BROKEN (.env.production): ```javascript CLERK_REDIRECT_URL=https://yourdomain.com/dashboard // But Clerk Dashboard has http://yourdomain.com (http not https) ```
FIXED (match protocol exactly): ```javascript CLERK_REDIRECT_URL=https://yourdomain.com // Ensure Clerk Dashboard lists: https://yourdomain.com ```
I'm uncertain whether: version < 4.20 requires explicit callback endpoint configuration vs. automatic handling in v4.20+. Check your package.json for @clerk/nextjs version and consult release notes if on legacy versions.
---
Still Broken? Check These Too
1. Multiple ClerkProvider instances – If you wrapped your app twice with <ClerkProvider>, Clerk's session state conflicts. Search your codebase for duplicate providers and keep only one at app.js root.
2. Middleware rules interfering – Next.js middleware or Vercel Edge Middleware might redirect before Clerk's callback completes. Add callback route to bypass list: ```javascript // middleware.ts export const config = { matcher: ['/((?!api/auth/callback).*)'], } ``` See [middleware best practices](/?guide=clerk-middleware).
3. Browser cache + old redirect URL in Service Worker – Clear service worker: DevTools → Application → Service Workers → Unregister, then npm run build and redeploy. Also check [session management guide](/?guide=clerk-sessions).
---
Immediate Actions (Before 3am)
1. Open Clerk Dashboard → Application → Settings
2. Copy all Allowed Redirect URLs
3. Match CLERK_REDIRECT_URL in .env.local, .env.production, and deployment platform (Vercel, Railway, etc.)
4. In browser DevTools → Application → Storage → Clear all localStorage + cookies for your domain
5. Hard refresh: Ctrl+Shift+R (Windows) or Cmd+Shift+R (Mac)
6. Redeploy if production
---
Official Documentation
[Clerk Redirect URLs Documentation](https://clerk.com/docs/deployments/manage-your-application)---
Found a different variation? Drop it in the comments below – variations like Auth0 callback issues, custom domain setups, or monorepo configs help everyone.