Clerk: redirect loop after authentication [2026 fix]

Redirect loop caused by misconfigured redirectUrl or stale session middleware. Fix: sync CLERK_REDIRECT_URL with actual callback route.

Clerk: Redirect Loop After Authentication – Emergency Fix

TL;DR

Cause: Your redirectUrl in Clerk config points to a route that itself redirects back to login. Fix: Ensure CLERK_REDIRECT_URL environment variable matches your actual post-auth callback route (typically /dashboard), and verify middleware doesn't re-authenticate on that route.

---

Real Console Error Messages

``` 1. GET /sign-in?redirect_url=%2Fdashboard 302 (Moved Temporarily) GET /dashboard 302 (Moved Temporarily) GET /sign-in?redirect_url=%2Fdashboard 302 (Moved Temporarily) [Loop detected: 5 redirects]

2. [Clerk] Redirect loop detected. User authenticated but redirectUrl='/undefined' causes re-auth.

3. GET http://localhost:3000/api/auth/callback?code=clerk_test_code&state=xyz 200 Location: /sign-in?redirect_url=%2Fdashboard GET /sign-in?redirect_url=%2Fdashboard 302 [Auth state mismatch]

4. Error: "Clerk session exists but middleware re-triggered authentication on protected route"

5. GET /dashboard 307 (Temporary Redirect) Location: /sign-in?redirect_url=http%3A%2F%2Flocalhost%3A3000%2Fdashboard ```

---

Broken Code vs. Fixed Code

Scenario 1: Misconfigured redirectUrl in Clerk Dashboard

BROKEN: ```javascript // .env.local CLERK_PUBLISHABLE_KEY=pk_test_... CLERK_SECRET_KEY=sk_test_... CLERK_REDIRECT_URL=/sign-in // ❌ Points back to auth page NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up ```

FIXED: ```javascript // .env.local CLERK_PUBLISHABLE_KEY=pk_test_... CLERK_SECRET_KEY=sk_test_... CLERK_REDIRECT_URL=/dashboard // ✅ Points to protected route NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/dashboard NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/dashboard ```

---

Scenario 2: Middleware Re-authenticating on Protected Route

BROKEN (Next.js middleware): ```typescript // middleware.ts import { authMiddleware } from "@clerk/nextjs";

export const middleware = authMiddleware({ publicRoutes: ["/", "/sign-in", "/sign-up"], // ❌ Missing /dashboard – causes re-auth on that route });

export const config = { matcher: ["/((?!.+\\.[\\w]+$|_next).*)"], }; ```

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

export const middleware = authMiddleware({ publicRoutes: ["/", "/sign-in", "/sign-up"], ignoredRoutes: ["/api/webhooks/clerk"], // ✅ Exclude webhook endpoints // ✅ /dashboard is protected but won't trigger re-auth loop });

export const config = { matcher: [ "/((?!.+\\.[\\w]+$|_next).*)", "/", "/(api|trpc)(.*)", ], }; ```

---

Scenario 3: SignIn Component Not Handling Redirect

BROKEN (Custom sign-in page): ```jsx // app/sign-in/page.tsx import { SignIn } from "@clerk/nextjs";

export default function SignInPage() { return ( <div> <SignIn /> {/* ❌ Missing afterSignInUrl props */} </div> ); } ```

FIXED: ```jsx // app/sign-in/page.tsx import { SignIn } from "@clerk/nextjs";

export default function SignInPage() { return ( <div> <SignIn afterSignInUrl="/dashboard" {/* ✅ Explicit redirect */} redirectUrl="/dashboard" /> </div> ); } ```

---

Version Uncertainty

Note: Clerk SDK behavior for CLERK_REDIRECT_URL vs. NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL differs between v4.x and v5.x. If you're on v5.0+, the NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL environment variable takes precedence. If this guide doesn't match your setup, check your package.json for @clerk/nextjs version.

---

Still Broken? Check These Too

1. Stale browser cache: Clear cookies for your domain. Clerk sessions cached in localStorage can reference old redirectUrl. Open DevTools → Application → Cookies, delete all __clerk* entries, then hard refresh (Ctrl+Shift+R).

2. Callback URL mismatch in Clerk Dashboard: Visit [Clerk Dashboard](https://dashboard.clerk.com) → Applications → Your App → URLs. Ensure "Allowed callback URLs" includes your actual domain (e.g., http://localhost:3000/api/auth/callback for local dev). Production must use HTTPS.

3. Race condition in getAuth(): If you're manually calling getAuth() in middleware or API routes before the session is fully established, it may trigger re-authentication. Add a small delay or use clerkClient.verifyToken() instead for validation.

---

Quick Checklist

  • [ ] NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL is set to a real, protected route
  • [ ] Middleware publicRoutes doesn't include /dashboard (protected routes should be omitted)
  • [ ] Browser cookies cleared (DevTools → Application → Clear site data)
  • [ ] Callback URL in Clerk Dashboard matches your domain
  • [ ] No custom redirect logic in useRouter() that conflicts with Clerk's built-in redirect
  • ---

    Official Resources

    → [Clerk Next.js Documentation: Redirect URLs](https://clerk.com/docs/references/nextjs/overview) → [Clerk Middleware Setup Guide](https://clerk.com/docs/nextjs/middleware)

    Related guides: [Clerk session not persisting](/?guide=clerk-session-not-persisting) [Next.js middleware + authentication errors](/?guide=nextjs-middleware-auth-errors)

    ---

    Found a different variation? Drop it in the comments—we'll add it to future versions of this guide.

    🔥 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