Clerk: redirect loop after authentication [2026 fix]

Redirect loop after Clerk auth usually means misconfigured redirect URIs or missing SignedIn wrapper. Fix: verify CLERK_REDIRECT_URL matches your deployment domain.

Clerk: Redirect Loop After Authentication — 2am Emergency Fix

TL;DR

Cause: Your CLERK_REDIRECT_URL environment variable doesn't match your actual deployment domain, or your app lacks proper <SignedIn> wrapper components. Fix: Update .env.local to CLERK_REDIRECT_URL=https://yourdomain.com (exact match, no trailing slash) and wrap protected routes with <SignedIn>.

---

Exact Error Messages You'll See

``` 1. ERR_TOO_MANY_REDIRECTS: net::ERR_TOO_MANY_REDIRECTS Chrome blocked access to yourdomain.com because it detected a redirect loop

2. [Clerk] Infinite redirect detected The redirect chain exceeded maximum length at /sign-in → /dashboard → /sign-in

3. ClerkRuntimeError: Redirect loop detected in middleware Middleware returned a redirect that matches the current pathname

4. Warning: You provided a redirect_url that does not match your instance's configured redirect URIs. Configured: http://localhost:3000

5. [nextjs] Unhandled rejection: Error: NEXT_REDIRECT called in a non-render context at Object.<anonymous> (middleware.ts:45) ```

---

Broken Code → Exact Fix

Problem 1: Environment Variable Mismatch

BROKEN: ```env

.env.local

CLERK_PUBLISHABLE_KEY=pk_test_xxx CLERK_SECRET_KEY=sk_test_xxx CLERK_REDIRECT_URL=http://localhost:3000

❌ But app actually runs on https://myapp.vercel.app

```

FIXED: ```env

.env.local (development)

CLERK_PUBLISHABLE_KEY=pk_test_xxx CLERK_SECRET_KEY=sk_test_xxx CLERK_REDIRECT_URL=http://localhost:3000

.env.production (in Vercel/hosting dashboard)

CLERK_PUBLISHABLE_KEY=pk_live_xxx CLERK_SECRET_KEY=sk_live_xxx CLERK_REDIRECT_URL=https://myapp.vercel.app

✅ Exact match, no trailing slash

```

Problem 2: Missing SignedIn Wrapper

BROKEN: ```tsx // app/dashboard/page.tsx import { useAuth } from "@clerk/nextjs";

export default function Dashboard() { const { isSignedIn } = useAuth(); // ❌ Component still renders during auth state transitions // causing infinite redirects between protected/public routes return ( <div> {isSignedIn ? <h1>Welcome</h1> : <p>Not signed in</p>} </div> ); } ```

FIXED: ```tsx // app/dashboard/page.tsx import { SignedIn, SignedOut, RedirectToSignIn } from "@clerk/nextjs";

export default function Dashboard() { return ( // ✅ Proper conditional rendering prevents redirect loops <> <SignedIn> <h1>Welcome to Dashboard</h1> {/* Protected content only renders when authenticated */} </SignedIn> <SignedOut> <RedirectToSignIn /> {/* Cleanly redirects unauthenticated users */} </SignedOut> </> ); } ```

Problem 3: Middleware Route Conflict

BROKEN: ```ts // middleware.ts import { authMiddleware } from "@clerk/nextjs";

export const config = { matcher: ["/", "/dashboard(.*)"], // ❌ Middleware protects /dashboard but also catches // redirect-to-sign-in requests, creating a loop };

export default authMiddleware({ publicRoutes: [], }); ```

FIXED: ```ts // middleware.ts import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";

const isProtectedRoute = createRouteMatcher([ "/dashboard(.*)", "/profile(.*)", ]);

export default clerkMiddleware((auth, req) => { if (isProtectedRoute(req)) auth().protect(); // ✅ Let Clerk handle auth checks without interfering // with internal sign-in redirects });

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

---

Still Broken? Check These Too

1. Clerk Dashboard URI Mismatch — Log into [dashboard.clerk.com](https://dashboard.clerk.com), go to Settings > URLs, and verify Authorized redirect URLs exactly match your CLERK_REDIRECT_URL. Include http://localhost:3000 for local dev AND https://yourdomain.com for production.

2. Stale Browser Cache — Hard-refresh with Cmd+Shift+R (Mac) or Ctrl+Shift+R (Windows). Clear cookies: DevTools → Application → Cookies → Delete all for your domain, then reload.

3. Multiple Clerk Providers — Check you're not wrapping your app with <ClerkProvider> twice (common in layouts). [Search for clerkProvider patterns](/?guide=nextjs-structure).

---

Version-Specific Notes

  • Clerk SDK v4.x+: Use clerkMiddleware() (new); authMiddleware() is deprecated.
  • Next.js 13-14: Middleware file location is ./middleware.ts at root (not in app/).
  • Next.js 15+: Behavior unchanged, but verify your matcher pattern doesn't conflict with /api/auth/* internal routes.
  • I'm uncertain about: Whether your specific hosting provider (Railway, Fly.io, AWS) auto-injects CLERK_REDIRECT_URL. Check their docs + your deployment logs.

    ---

    Quick Verification

    After fixing, test this flow: 1. Visit /dashboard → redirects to /sign-in ✓ 2. Sign in → redirects to /dashboard (ONE redirect, not looped) ✓ 3. Sign out → redirects to / ✓ 4. Browser Network tab shows no more than 2 redirects per action ✓

    Official Docs: [Clerk Next.js Redirect Issues](https://clerk.com/docs/nextjs/troubleshooting)

    Related Guide: [Clerk authentication setup from scratch](/?guide=clerk-setup) Related Guide: [Fixing 'user is undefined' after Clerk auth](/?guide=clerk-undefined)

    ---

    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