Clerk: redirect loop after authentication [2026 fix]
Redirect loop after Clerk auth caused by misconfigured callback URL or stale session state. Fix: Verify CLERK_REDIRECT_URL matches your domain and clear browser storage.
Clerk: Redirect Loop After Authentication [2am Emergency Fix]
TL;DR
Cause: Your Clerk redirect URL doesn't match your actual domain, or the session state is corrupted in browser storage. Fix: SetCLERK_REDIRECT_URL=https://yourdomain.com in .env.local and clear localStorage/sessionStorage in DevTools.---
Real Console Error Messages
You'll see one or more of these exact errors:
``` Error: Redirect loop detected. Ensure CLERK_REDIRECT_URL is correctly configured. ```
``` undefined (reading 'redirectUrl') at AuthenticatedRoute.tsx:45 ```
``` warning: getAuth() returned null after redirect. Session may not be initialized. ```
``` [Clerk] Invalid redirect URI. Expected https://yourdomain.com but got http://localhost:3000 ```
``` Cookies are blocked or third-party cookies disabled. Auth session cannot persist. ```
---
Broken Code vs. Fixed Code
Problem 1: Missing or Mismatched Redirect URL
BROKEN: ```javascript // .env.local (WRONG - localhost only) CLERK_PUBLISHABLE_KEY=pk_test_... CLERK_SECRET_KEY=sk_test_... // CLERK_REDIRECT_URL is not set!
// pages/auth.tsx import { useAuth } from '@clerk/nextjs';
export default function AuthPage() { const { isSignedIn } = useAuth(); if (!isSignedIn) { return <SignInButton />; } return <Dashboard />; // Loops infinitely } ```
FIXED: ```javascript // .env.local (CORRECT - matches your actual domain) CLERK_PUBLISHABLE_KEY=pk_test_... CLERK_SECRET_KEY=sk_test_... CLERK_REDIRECT_URL=https://yourdomain.com NEXT_PUBLIC_CLERK_REDIRECT_URL=https://yourdomain.com
// pages/auth.tsx (with redirect guard) import { useAuth } from '@clerk/nextjs'; import { useRouter } from 'next/router'; import { useEffect } from 'react';
export default function AuthPage() { const { isSignedIn, isLoaded } = useAuth(); const router = useRouter(); useEffect(() => { if (isLoaded && isSignedIn) { router.push('/dashboard'); // Explicit redirect, not component return } }, [isLoaded, isSignedIn, router]); if (!isLoaded) return <div>Loading...</div>; if (isSignedIn) return null; // Return nothing while redirecting return <SignInButton />; } ```
Problem 2: Stale Session State in Browser
BROKEN: ```javascript // Middleware caches old session before env var was updated // middleware.ts import { authMiddleware } from '@clerk/nextjs';
export const config = { matcher: ['/((?!.+\\.[\\w]+$|_next).*)', '/'], };
export default authMiddleware(); // This uses cached redirectUrl from old environment ```
FIXED: ```javascript // middleware.ts - with explicit redirect handling import { authMiddleware, redirectToSignIn } from '@clerk/nextjs';
export const config = { matcher: ['/((?!.+\\.[\\w]+$|_next).*)', '/'], };
export default authMiddleware({ publicRoutes: ['/'], afterAuth(auth, req) { // If not authenticated and trying to access protected route if (!auth.userId && !auth.isPublicRoute) { return redirectToSignIn({ returnBackUrl: req.url }); } }, }); ```
THEN: In your browser DevTools (F12 → Application tab): ```javascript // Clear all Clerk-related storage Object.keys(localStorage).forEach(key => { if (key.includes('clerk') || key.includes('__clerk')) { localStorage.removeItem(key); } }); Object.keys(sessionStorage).forEach(key => { if (key.includes('clerk')) { sessionStorage.removeItem(key); } }); // Then reload page location.reload(); ```
---
Still Broken? Check These Too
1. Third-party cookies blocked in browser – Clerk requires cookies. Check DevTools → Settings → Cookies and disable "Block third-party cookies" for testing. For production, add your domain to allow list. See [related cookie debugging guide](/?guide=cookies).
2. Development vs. production mismatch – If CLERK_REDIRECT_URL=https://yourdomain.com but your app is on subdomain.yourdomain.com, Clerk rejects it as invalid. Update env var to match exactly, including subdomain. I'm uncertain if Clerk v0.19+ allows wildcard subdomains; check official docs.
3. SignInButton/SignUpButton missing callback prop – Some versions require explicit redirectUrl prop. Test with: <SignInButton redirectUrl="/dashboard" />. This behavior may vary between versions. See [authentication flow debugging](/?guide=auth-flow).
---
Found a Different Variation?
Drop it in the comments below! We update this guide based on real 2am incidents. Please include: (1) your Clerk SDK version, (2) exact error message, (3) your framework/version.
---