Clerk: redirect loop after authentication [2026 fix]

Redirect loop after Clerk auth caused by misconfigured callback URLs or stale session state. Fix: verify CLERK_REDIRECT_URL matches your domain exactly.

Clerk: Redirect Loop After Authentication – Emergency Fix

TL;DR

Cause: Callback URL in Clerk dashboard doesn't match your actual domain or middleware is redirecting authenticated users back to login. Fix: Set CLERK_REDIRECT_URL to your exact production domain (with protocol) and ensure your auth middleware only redirects *unauthenticated* users.

---

Real Console Error Messages

Look for these exact errors in your browser console and server logs at 2am:

``` 1. ERR_TOO_MANY_REDIRECTS: Redirect loop detected. The page isn't redirecting properly.

2. Uncaught (in promise) Error: Redirect loop - user authenticated but redirected to /sign-in

3. GET http://localhost:3000/sign-in 302 Found → http://localhost:3000/sign-in (infinite redirect)

4. [Clerk] Redirect URL mismatch: expected https://example.com/auth/callback, got http://example.com/auth/callback

5. useAuth() returned isLoaded: false after successful authentication - session stale ```

---

Broken Code vs. Exact Fix

Problem 1: Wrong Callback URL

BROKEN: ```javascript // .env.local (wrong - using localhost for production) CLERK_PUBLISHABLE_KEY=pk_live_... CLERK_SECRET_KEY=sk_live_... CLERK_REDIRECT_URL=http://localhost:3000/auth/callback ```

FIXED: ```javascript // .env.local (correct - matches production domain exactly) CLERK_PUBLISHABLE_KEY=pk_live_... CLERK_SECRET_KEY=sk_live_... CLERK_REDIRECT_URL=https://yourdomain.com/auth/callback ```

Problem 2: Middleware Redirecting Authenticated Users

BROKEN: ```javascript // middleware.ts - redirects everyone to sign-in export default clerkMiddleware(async (auth, req) => { const { userId } = await auth(); // BUG: redirects even if authenticated if (!req.nextUrl.pathname.startsWith('/sign-in')) { return NextResponse.redirect(new URL('/sign-in', req.url)); } }); ```

FIXED: ```javascript // middleware.ts - only redirect unauthenticated users export default clerkMiddleware(async (auth, req) => { const { userId } = await auth(); const protectedRoutes = ['/dashboard', '/profile', '/settings']; if (!userId && protectedRoutes.some(route => req.nextUrl.pathname.startsWith(route))) { return NextResponse.redirect(new URL('/sign-in', req.url)); } });

export const config = { matcher: [ '/((?!_next/static|_next/image|favicon.ico|public).*)', ], }; ```

Problem 3: Stale Session After Sign-In

BROKEN: ```javascript // pages/dashboard.tsx - checking session too early import { useAuth } from '@clerk/nextjs';

export default function Dashboard() { const { userId, isLoaded } = useAuth(); // BUG: component renders before isLoaded is true if (!userId) return <Redirect to="/sign-in" />; return <div>Welcome {userId}</div>; } ```

FIXED: ```javascript // pages/dashboard.tsx - wait for session hydration import { useAuth } from '@clerk/nextjs'; import { useRouter } from 'next/navigation';

export default function Dashboard() { const { userId, isLoaded } = useAuth(); const router = useRouter(); // FIXED: only render after auth state is loaded if (!isLoaded) { return <div>Loading authentication...</div>; } if (!userId) { router.push('/sign-in'); return null; } return <div>Welcome {userId}</div>; } ```

---

Verification Checklist

1. Dashboard callback URLs (https://dashboard.clerk.com): - Go to Settings → Application URLs - Verify "Redirect URLs" includes: https://yourdomain.com/auth/callback - For development: also add http://localhost:3000/auth/callback

2. Environment variables: ```bash echo $CLERK_REDIRECT_URL # should show your production domain ```

3. Middleware order: - Clerk middleware must run *before* your auth checks - NextAuth/sessions middleware conflict? Remove competing auth.

---

Still Broken? Check These Too

1. Mixed HTTP/HTTPS: - Clerk live keys only work on HTTPS - If your dashboard uses HTTPS but callback is HTTP, it fails silently - Always use https://yourdomain.com

2. Multiple Auth Providers Conflict: - Check if you have NextAuth + Clerk both active - Remove one: [NextAuth + Clerk guide](/?guide=nextauth-clerk-conflict) - Ensure only one auth middleware is exported from /middleware.ts

3. Session Cookie Domain Mismatch: - If running on subdomain (api.example.com), Clerk may not persist session - Verify CLERK_COOKIE_DOMAIN=example.com in production - Browser DevTools → Application → Cookies → check __clerk_* cookies exist

---

Version Uncertainty

Note: This guide covers @clerk/nextjs v4.x and v5.x (2024-2026). The middleware API changed significantly between v3 and v4. If you're on v3, the clerkMiddleware() syntax differs—consult the [v3 migration docs](https://clerk.com/docs/upgrade-guides/nextjs).

---

Resources

  • [Official Clerk Redirect URL Docs](https://clerk.com/docs/deployments/clerk-application-urls)
  • [Related: Clerk session not persisting](/?guide=clerk-session-persist)
  • [Related: NextAuth vs Clerk comparison](/?guide=auth-comparison)
  • ---

    Found a different variation? Drop it in the comments—redirect loops have creative new causes every month. Tag your Clerk version + framework when reporting.

    ---

    *Last updated: 2026 | Still stuck at 3am? Check Clerk's status page or file an issue with exact redirect chain from DevTools Network tab.*

    🔥 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