Clerk: redirect loop after authentication [2026 fix]
Clerk redirect loop caused by misconfigured callback URLs or middleware ordering. Fix: verify CLERK_REDIRECT_URL matches your deployment domain exactly.
Clerk: Redirect Loop After Authentication [2026 Fix]
TL;DR
Cause: YourCLERK_REDIRECT_URL environment variable doesn't match your actual deployment domain or middleware is processing redirects before Clerk completes authentication.Fix: Set CLERK_REDIRECT_URL=https://yourdomain.com (exact match, no trailing slash) and ensure Clerk middleware runs *before* custom auth logic.
---
Exact Error Messages You'll See
``` 1. GET /api/auth/callback?code=XXX&state=YYY HTTP/1.1 → GET / HTTP/1.1 (infinite redirect chain)
2. Error: Invalid redirect_uri. The redirect_uri does not match any configured URLs.
3. [Clerk] Callback handler failed: redirect_uri parameter missing or invalid
4. POST /api/auth/callback 308 - middleware redirect loop detected
5. UnauthorizedError: Could not verify JWT. Session invalid or expired (after redirect loop) ```
---
Broken Code → Fixed Side by Side
Problem 1: Environment Variable Mismatch
❌ BROKEN (.env.production): ```env CLERK_PUBLISHABLE_KEY=pk_live_abc123xyz CLERK_SECRET_KEY=sk_live_def456uvw CLERK_REDIRECT_URL=http://localhost:3000 NEXT_PUBLIC_CLERK_REDIRECT_URL=localhost:3000 ```
✅ FIXED (.env.production): ```env CLERK_PUBLISHABLE_KEY=pk_live_abc123xyz CLERK_SECRET_KEY=sk_live_def456uvw CLERK_REDIRECT_URL=https://yourdomain.com NEXT_PUBLIC_CLERK_REDIRECT_URL=https://yourdomain.com ```
Why: The redirect URI in your environment must *exactly* match what's configured in [Clerk Dashboard](https://dashboard.clerk.com) → Applications → Allowed Redirect URLs. Protocol (https), domain, and port must all match. At 2am, 90% of cases are localhost in production config.
---
Problem 2: Middleware Processing Order (Next.js)
❌ BROKEN (middleware.ts): ```typescript import { authMiddleware } from "@clerk/nextjs/server"; import { customAuthCheck } from "@/lib/auth";
export const middleware = (req) => { customAuthCheck(req); // Runs FIRST - blocks Clerk callback return authMiddleware()(req); };
export const config = { matcher: ["/(.*)"], }; ```
✅ FIXED (middleware.ts): ```typescript import { authMiddleware } from "@clerk/nextjs/server"; import { customAuthCheck } from "@/lib/auth";
export const middleware = authMiddleware((auth, req) => { // Clerk processes callback FIRST if (req.nextUrl.pathname.startsWith("/api/auth/")) { return; // Let Clerk handle all auth callbacks } customAuthCheck(req); // Now safe to run custom logic });
export const config = { matcher: [ "/((?!_next|static|.*\\.png|.*\\.jpg).*)", "/api/(.*)", ], }; ```
Why: Custom middleware running *before* authMiddleware can intercept the /api/auth/callback request, preventing Clerk from completing the OAuth flow. Always wrap custom logic *inside* the Clerk middleware function.
---
Problem 3: Cookie Domain Mismatch (Multi-Domain Setup)
❌ BROKEN (ClerkProvider setup): ```tsx <ClerkProvider publishableKey={process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY} // Missing or wrong domain config > {children} </ClerkProvider> ```
✅ FIXED (app.tsx or _app.tsx): ```tsx <ClerkProvider publishableKey={process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY} domain={process.env.NEXT_PUBLIC_CLERK_DOMAIN || "yourdomain.com"} isSatellite={process.env.NEXT_PUBLIC_CLERK_IS_SATELLITE === "true"} > {children} </ClerkProvider> ```
Why: If you're using multiple subdomains (auth.yourdomain.com + app.yourdomain.com), Clerk needs explicit domain config so session cookies work across all properties. Without it, auth succeeds on one domain but fails on redirect to another.
---
Still Broken? Check These Too
1. Vercel/Hosting Deploy Mismatch — Is CLERK_REDIRECT_URL set in your hosting platform's environment variables? Hardcoded .env won't override. Log in to Vercel/Netlify/AWS and verify production env vars match your dashboard config.
2. Clerk Dashboard Allowed URLs — Go to [Clerk Dashboard](https://dashboard.clerk.com) → Applications → Your app → scroll to *Allowed Redirect URLs*. Add your exact production URL (including https://). If you deployed to a preview domain, add that too.
3. Session Token Expiration During Redirect — If you're redirecting to a protected route immediately after auth, the session token may not be validated in time. Add a 500ms delay or use Clerk's useAuth() hook to check isLoaded before rendering protected content. See [related](/?guide=clerk-session-verification) troubleshooting.
---
Version Note
This guide covers @clerk/nextjs v5.0+ and @clerk/remix v2.0+. Behavior differs significantly from v4.x regarding middleware. If you're on v4, ensure you're using withClerkMiddleware() instead of authMiddleware(). I'm uncertain if older Clerk SDK versions had different redirect handling — please verify your version with npm list @clerk/nextjs.
---
Official Docs
Related Guides
---
Found a different variation? Drop it in the comments. We're updating this guide in real-time based on the 2am incidents we see.