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