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: YourCLERK_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
clerkMiddleware() (new); authMiddleware() is deprecated. ./middleware.ts at root (not in app/). 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.