Clerk: redirect loop after authentication [2026 fix]
Redirect loop after Clerk auth caused by misconfigured redirectUrl or missing middleware. Fix: verify CLERK_REDIRECT_URL env var and ensure middleware runs before auth routes.
Clerk: Redirect Loop After Authentication - Emergency Fix Guide
TL;DR
Problem: Your app authenticates successfully but immediately redirects back to the login page, creating an infinite loop. Fix: Verify yourCLERK_REDIRECT_URL environment variable matches your actual domain and ensure Clerk middleware executes before your protected routes.---
Exact Error Messages from Console
You'll see one or more of these in your browser console or server logs at 2am:
``` [Clerk] Redirect loop detected: redirectUrl does not match origin ```
``` Error: Invalid redirect URL. Expected origin to match CLERK_REDIRECT_URL, got http://localhost:3000 but config has https://example.com ```
``` (uncaught in promise) TypeError: Cannot read property 'redirectUrl' of undefined at _authenticateRequest ```
``` [Clerk] Auth session created but middleware skipped - redirect to /sign-in failed ```
``` WARN: afterAuthUrl not reachable. Infinite redirect detected. Fallback to /dashboard ```
---
Broken Code vs. Exact Fix
Issue #1: Wrong Environment Variable
BROKEN: ```javascript // .env.local (WRONG - includes /auth path) CLERK_REDIRECT_URL=https://example.com/auth CLERK_PUBLISHABLE_KEY=pk_live_... CLERK_SECRET_KEY=sk_live_... ```
FIXED: ```javascript // .env.local (CORRECT - base domain only) CLERK_REDIRECT_URL=https://example.com CLERK_PUBLISHABLE_KEY=pk_live_... CLERK_SECRET_KEY=sk_live_... ```
---
Issue #2: Middleware Not Running on Protected Routes
BROKEN (Next.js App Router): ```javascript // middleware.ts - placed incorrectly or not configured import { auth } from '@clerk/nextjs/server';
export default async function middleware(request) { // This only runs on public routes! return NextResponse.next(); }
export const config = { matcher: ['/public/:path*'] // TOO RESTRICTIVE }; ```
FIXED: ```javascript // middleware.ts - correct configuration import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
const isProtectedRoute = createRouteMatcher(['/dashboard(.*)', '/profile(.*)']);
export default clerkMiddleware((auth, req) => { if (isProtectedRoute(req)) { auth().protect(); } });
export const config = { matcher: ['/((?!.+\\.[\\w]+$|_next).*)', '/', '/(api|trpc)(.*)'] }; ```
---
Issue #3: ClerkProvider afterSignInUrl Mismatch
BROKEN: ```javascript // app.tsx or root layout <ClerkProvider afterSignInUrl="/dashboard" afterSignUpUrl="/onboarding" // Missing signInUrl causes redirect loop fallback > <App /> </ClerkProvider> ```
FIXED: ```javascript // app.tsx - explicit URLs that match your app structure <ClerkProvider publishableKey={process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY} signInUrl="/sign-in" signUpUrl="/sign-up" afterSignInUrl="/dashboard" afterSignUpUrl="/onboarding" > <App /> </ClerkProvider> ```
---
Root Cause Analysis
Redirect loops occur when:
1. Domain mismatch: Your CLERK_REDIRECT_URL points to a different domain than where your app is running
2. Middleware skipped: Clerk's authentication middleware doesn't execute before route handlers, leaving the session unverified
3. Infinite afterSignInUrl chain: Your afterSignInUrl redirects back to /sign-in instead of a protected page
4. Missing origin header: Deployed apps sometimes fail to pass correct origin in requests
---
Still Broken? Check These Too
1. Cookie Domain Configuration
If you have multiple subdomains, ensure cookies are set correctly: ```javascript // In Clerk Dashboard → Advanced → Cookie Settings // Set to: .example.com (with leading dot for subdomains) ```2. Development vs. Production Environment Variables
Verify your deployed environment actually hasCLERK_REDIRECT_URL set:
```bash
Vercel example
vercel env pull .env.local grep CLERK_REDIRECT_URL .env.local ```3. Auth State Hydration Race Condition
Add defensive checks in your protected components: ```javascript const DashboardPage = () => { const { isLoaded, userId } = useAuth(); if (!isLoaded) return <LoadingSpinner />; // Wait for hydration if (!userId) return <Navigate to="/sign-in" />; // Explicit redirect return <Dashboard />; }; ```---
Quick Verification Checklist
CLERK_REDIRECT_URL matches your production domain exactly (no paths, no trailing slash)src/middleware.ts (Next.js) or correct location for your frameworkclerkMiddleware() is imported from @clerk/nextjs/server, not deprecated @clerk/nextjscreateRouteMatcher()afterSignInUrl points to a route that exists and doesn't redirect back to auth---
Official Documentation
Refer to the [Clerk Next.js Setup Guide](https://clerk.com/docs/quickstarts/nextjs) and [Middleware Configuration](https://clerk.com/docs/references/nextjs/clerk-middleware) for your specific framework version.
Also see our related guides: [Clerk authentication fails silently](/?guide=clerk-silent-fail) [Session persistence issues in production](/?guide=clerk-session-prod)
---
Found a different variation? Drop it in the comments
If you encountered a redirect loop with a different root cause or you're using Express, Remix, or another framework, please share your error messages and setup in the comments below. Community fixes help everyone at 2am! 🚀