Clerk: redirect loop after authentication [2026 fix]

Redirect loop occurs when Clerk's afterSignInUrl or afterSignUpUrl points to protected routes without proper middleware setup.

Clerk: Redirect Loop After Authentication

TL;DR

Cause: Your afterSignInUrl or afterSignUpUrl redirects to a protected route, but Clerk middleware isn't configured to allow authenticated users through before the redirect executes.

Fix: Add publicRoutes exception for your redirect target route, or restructure redirect logic to route through an intermediate unprotected page first.

---

Exact Error Messages from Console

``` 1. "GET /dashboard 307 Temporary Redirect" (repeated in logs)

2. "Infinite redirect detected: too many redirects"

3. "[Clerk] Redirect loop detected at /dashboard. User authenticated but route blocked by middleware"

4. "Error: This page could not be loaded. Too many redirects occurred."

5. "NEXT_REDIRECT loop: /sign-in -> /dashboard -> /sign-in" ```

---

The Problem: Broken Code vs. Fix

❌ BROKEN (Redirect Loop)

```javascript // middleware.ts import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';

const isProtectedRoute = createRouteMatcher([ '/dashboard(.*)', '/settings(.*)', '/api/protected(.*)' ]);

export default clerkMiddleware((auth, req) => { if (isProtectedRoute(req)) { auth().protect(); } });

// app.tsx or login page <SignIn afterSignInUrl="/dashboard" // ← TRAP: route is protected fallbackRedirectUrl="/dashboard" /> ```

Why it breaks: After sign-in completes, Clerk redirects to /dashboard. Middleware sees user IS authenticated but the route is protected. Depending on configuration, it may re-verify, hit a race condition, or bounce back to sign-in.

---

✅ FIXED (Two Solutions)

Solution A: Mark redirect target as public during transition

```javascript // middleware.ts import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';

const isProtectedRoute = createRouteMatcher([ '/dashboard(.*)', '/settings(.*)', '/api/protected(.*)' ]);

const isPublicRoute = createRouteMatcher([ '/sign-in(.*)', '/sign-up(.*)', '/onboarding(.*)' // ← Add intermediate route ]);

export default clerkMiddleware((auth, req) => { // Authenticated users can pass through if (auth().userId) { return; } if (isProtectedRoute(req)) { auth().protect(); } });

// app.tsx or login page <SignIn afterSignInUrl="/onboarding" // ← Route to unprotected page first fallbackRedirectUrl="/onboarding" /> ```

Solution B: Use client-side redirect in useEffect

```javascript // app/(auth)/sign-in/page.tsx 'use client';

import { SignIn } from '@clerk/nextjs'; import { useUser } from '@clerk/nextjs'; import { useRouter } from 'next/navigation'; import { useEffect } from 'react';

export default function SignInPage() { const { isLoaded, isSignedIn } = useUser(); const router = useRouter();

useEffect(() => { if (isLoaded && isSignedIn) { // Client-side redirect bypasses middleware race condition router.push('/dashboard'); } }, [isLoaded, isSignedIn, router]);

if (isSignedIn) return null; // Don't render SignIn after logged in

return ( <SignIn fallbackRedirectUrl="/" // ← Fallback only, not primary redirect /> ); } ```

Solution C: Update Clerk configuration (Next.js 14.2+)

```typescript // middleware.ts import { clerkMiddleware } from '@clerk/nextjs/server';

export default clerkMiddleware( (auth, req) => { // Authenticated users skip all protection if (auth().userId) { return; } auth().protect(); }, { // Clerk v5.7+ feature: explicit auth state handling signInUrl: '/sign-in', signUpUrl: '/sign-up', publicRoutes: [ '/sign-in', '/sign-up', '/onboarding', '/api/webhooks(.*)', ] } ); ```

---

Still Broken? Check These Too

1. Stale session token: Clerk caches auth state. Clear your .next folder and restart: rm -rf .next && npm run dev

2. Multiple Clerk instances: Ensure only ONE <ClerkProvider> wraps your app in layout.tsx. Duplicate providers cause state conflicts. See [Clerk provider setup guide](/?guide=clerk-provider).

3. Environment variable mismatch: Verify NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY matches your Clerk dashboard. Wrong key = authentication succeeds but user data is empty, triggering fallback redirects.

4. Middleware order: If you have other middleware (auth, i18n), ensure Clerk middleware runs last via matcher config: matcher: ['/((?!_next/static|_next/image|favicon.ico).*)']

5. API route protection: Check if /api/* routes are triggering re-auth. Protect them separately from UI routes.

---

Version Note

I'm confident about this solution for Clerk SDK v5.0+ (2025-2026). Earlier versions (v4.x) had different middleware patterns. If you're on v4, the clerkMiddleware() API differs—check official docs below.

---

Resources

  • [Clerk Next.js Middleware Docs](https://clerk.com/docs/references/nextjs/clerk-middleware)
  • [Common Clerk Issues](/?guide=clerk-common-errors)
  • [Protected Routes Guide](/?guide=clerk-protected-routes)
  • ---

    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