Clerk: redirect loop after authentication [2026 fix]
Redirect loop after Clerk auth caused by misconfigured redirect URIs or missing middleware. Fix: verify REDIRECT_URL matches exactly and ensure ClerkMiddleware runs before route handlers.
Clerk: Redirect Loop After Authentication
TL;DR
Cause: YourREDIRECT_URL environment variable doesn't match your actual app's callback route, or ClerkMiddleware isn't configured before your route handlers execute.Fix: Ensure REDIRECT_URL=https://yourdomain.com/auth/callback (exact domain/port match) and move ClerkMiddleware to run first in your middleware stack.
---
Common Console Error Messages
If you're seeing these exact errors, you're in the right place:
``` Error: Redirect loop detected. The redirect chain exceeded 5 hops at https://yourdomain.com/auth/callback
Unauthorized: Missing or invalid Clerk session. Redirecting to sign-in, which redirects back here.
TypeError: Cannot read property 'createdAt' of undefined at Clerk.sessions
warning: Clerk: The value of 'redirect_url' could not be matched to any configured sign-in URIs
Infinite redirect detected between /sign-in and /auth/callback ```
---
Broken Code → Exact Fix
Issue #1: Mismatched REDIRECT_URL
BROKEN: ```javascript // .env.local REDIRECT_URL=http://localhost:3000/callback
// Your actual route app.get('/auth/callback', (req, res) => { // handles clerk callback }); ```
Problem: Route is /auth/callback but env var points to /callback.
FIXED: ```javascript // .env.local REDIRECT_URL=http://localhost:3000/auth/callback
// Your actual route app.get('/auth/callback', (req, res) => { // handles clerk callback }); ```
Key detail: Port must match too. If your dev server runs on :3001, use that exact port in REDIRECT_URL.
---
Issue #2: ClerkMiddleware Not Running First
BROKEN: ```javascript // pages/_app.tsx (Next.js) export default function App({ Component, pageProps }) { return ( <Layout> <Component {...pageProps} /> </Layout> ); }
// middleware.ts (but ClerkMiddleware added after other middleware) export default clerkMiddleware(); export const config = { matcher: ['/api/(.*)', '/trpc/(.*)'], }; ```
Problem: Your matcher is too narrow and doesn't protect auth routes.
FIXED: ```javascript // middleware.ts (Next.js 13+) 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: ['/((?!.*\\..*|_next).*)', '/', '/(api|trpc)(.*)'] }; ```
Critical: The matcher regex must come *first* in your middleware pipeline, before any other auth checks.
---
Issue #3: Production Domain Mismatch
BROKEN: ```javascript // .env.production REDIRECT_URL=http://localhost:3000/auth/callback ```
FIXED: ```javascript // .env.production REDIRECT_URL=https://youractual-domain.com/auth/callback ```
Uncertainty note: If you're using a proxy or reverse-proxy (Nginx, Cloudflare Workers), the redirect URL should match what users see in their browser address bar, not your internal server address. We cannot guarantee behavior if your infrastructure differs from standard deployments—test in staging first.
---
Still Broken? Check These Too
1. Clerk allowlist mismatch: In your Clerk dashboard, verify the "Redirect URIs" under Application Settings exactly match your REDIRECT_URL. Case matters. Trailing slashes matter. Typos in domain TLD will cause loops.
2. Multiple REDIRECT_URL values: Some frameworks auto-generate callback URLs. Check if your framework (Next.js, Remix, SvelteKit) is creating an *additional* callback route you're not aware of. Run npm run build in verbose mode and search for "callback" in output.
3. Auth state corruption: Clear your browser's local storage (localStorage.clear() in console) and cookies (Clerk session cookie usually named __session or __clerk_*). Stale auth state can cause phantom redirects even with correct config.
---
Related Issues
---
Official Documentation
[Clerk Authentication & Redirects →](https://clerk.com/docs/custom-flows/callback-urls)
---
At 2am, the fastest fix: Copy your REDIRECT_URL exactly as it appears in your Clerk dashboard under "Redirect URIs", paste it into .env.local, restart your dev server, and test. 90% of cases resolve immediately.
Found a different variation? Drop it in the comments.