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