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: YourredirectUrl 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 routepublicRoutes doesn't include /dashboard (protected routes should be omitted)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.