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