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 Clerk afterSignInUrl 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

  • [Clerk Middleware Setup Guide](/?guide=clerk-middleware)
  • [Next.js Protected Routes Pattern](/?guide=nextjs-auth-routes)
  • [Official Clerk Docs: Redirect After Sign In](https://clerk.com/docs/references/nextjs/clerk-provider#signs-in-url)
  • 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.

    🔥 0d
    LIVE
    PlanetScale rage spiking Vercel pricing complaints Railway gaining fast Supabase happiness rising Resend loved by devs PlanetScale rage spiking Vercel pricing complaints Railway gaining fast Supabase happiness rising
    DEVELOPER PAIN RADAR // Loading...

    Developers complain.
    Opportunities appear.

    We track what developers are struggling with today — and what opportunities that creates.

    guides today
    avg happiness
    🔥 Pain
    📖 Guides
    🔭 Explore
    👤 Mine
    🔥 Pain Radar — rage scores today
    ↗ share
    💡 Opportunity Feed — pain = market gap
    📈 Tool Momentum
    all scores →
    📖 Latest Guide
    all guides →
    📖 All Guides
    📊 Tool Scores
    + Submit
    📰 Hacker News
    ➕ Submit a Tool
    ← back