Clerk: redirect loop after authentication [2026 fix]
Clerk redirect loop occurs when `afterSignInUrl` or middleware redirects conflict. Fix: ensure `signInUrl` points to `/sign-in` and remove duplicate redirects from middleware.
Clerk: Redirect Loop After Authentication
TL;DR
Cause: Your ClerkafterSignInUrl or middleware is redirecting authenticated users back to the sign-in page instead of the intended destination.
Fix: Verify signInUrl is /sign-in, remove circular redirects from middleware, and ensure afterSignInUrl points to a protected route that doesn't re-trigger auth checks.---
Exact Error Messages
``` 1. ERR_TOO_MANY_REDIRECTS: Chrome detected too many redirects in a row 2. [Clerk] Not authenticated. Redirecting to /sign-in 3. Infinite loop detected: /sign-in → /dashboard → /sign-in 4. useAuth() hook returned null after successful sign-in callback 5. RedirectToSignIn triggered unexpectedly for authenticated user ```
---
Broken Code vs. Fix
Issue #1: Middleware Redirect Conflict
BROKEN: ```javascript // middleware.ts (Next.js 13+) export default authMiddleware({ publicRoutes: ['/'], ignoredRoutes: ['/sign-in', '/sign-up'], afterAuth(auth, req) { // WRONG: This redirects authenticated users BACK to sign-in if (!auth.userId) { return redirectToSignIn({ returnBackUrl: req.url }); } // WRONG: Circular redirect if (auth.userId && req.nextUrl.pathname === '/') { return NextResponse.redirect(new URL('/sign-in', req.url)); } }, }); ```
FIXED: ```javascript // middleware.ts (Next.js 13+) export default authMiddleware({ publicRoutes: ['/', '/sign-in', '/sign-up'], ignoredRoutes: ['/api/webhooks(.*)', '/health'], afterAuth(auth, req) { // CORRECT: Only redirect unauthenticated users if (!auth.userId && req.nextUrl.pathname !== '/sign-in') { return redirectToSignIn({ returnBackUrl: req.url }); } // CORRECT: Redirect authenticated users away from sign-in if (auth.userId && req.nextUrl.pathname === '/sign-in') { return NextResponse.redirect(new URL('/dashboard', req.url)); } }, }); ```
Issue #2: Conflicting afterSignInUrl Configuration
BROKEN: ```javascript // app.tsx or _app.tsx <ClerkProvider publishableKey={process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY} appearance={{ elements: { rootBox: 'w-full', }, }} > {/* WRONG: afterSignInUrl points to route that redirects back */} <SignIn afterSignInUrl="/" fallbackRedirectUrl="/sign-in" /> </ClerkProvider> ```
FIXED: ```javascript // app.tsx or _app.tsx <ClerkProvider publishableKey={process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY} appearance={{ elements: { rootBox: 'w-full', }, }} > {/* CORRECT: afterSignInUrl points to protected dashboard */} <SignIn afterSignInUrl="/dashboard" fallbackRedirectUrl="/dashboard" /> </ClerkProvider> ```
Issue #3: useAuth() in Protected Route
BROKEN: ```javascript // app/dashboard/page.tsx export default function Dashboard() { const { isLoaded, isSignedIn } = useAuth(); // WRONG: This component renders before auth state loads, // then useAuth() triggers redirect if (!isSignedIn) { return <RedirectToSignIn />; // Called on every render } return <div>Dashboard</div>; } ```
FIXED: ```javascript // app/dashboard/page.tsx export default function Dashboard() { const { isLoaded, isSignedIn } = useAuth(); // CORRECT: Wait for auth to load before checking if (!isLoaded) return <div>Loading...</div>; // CORRECT: Middleware handles redirect, this is just a fallback if (!isSignedIn) return null; return <div>Dashboard</div>; } ```
---
Still Broken? Check These Too
1. Environment Variables Mismatch: Verify NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY and CLERK_SECRET_KEY are correctly set in .env.local. A mismatched key prevents proper auth state sync, causing redirects to fail.
2. Stale Session/Cookies: Clear your browser cache and cookies for localhost, then hard refresh (Ctrl+Shift+R or Cmd+Shift+R). Sometimes Clerk's session tokens don't refresh properly on code changes.
3. Multiple Auth Providers: If you're using both Clerk and another auth library (like Auth0, NextAuth), they may conflict. Ensure only one provider wraps your app. Check [authentication patterns guide](/?guide=multi-auth) for safe patterns.
4. Sign-In Component Inside Protected Route: Never place <SignIn /> on a route protected by middleware—it creates instant loops. Keep sign-in pages in public routes only.
5. returnBackUrl Misconfiguration: The returnBackUrl in redirectToSignIn() must point to a real, accessible route. Invalid URLs cause undefined behavior.
---
Related Resources
Note on Version Specificity: This guide covers Clerk SDK v4.x–v5.x (2024–2026). Earlier v3 versions use withClerkMiddleware() instead of authMiddleware()—check your package.json version if this doesn't match your setup.
---
Found a different variation? Drop it in the comments.