Clerk: redirect loop after authentication [2026 fix]
Your redirect URI doesn't match Clerk's allowlist or middleware runs after auth—add exact URI match and reorder Next.js middleware.
Clerk: Redirect Loop After Authentication - 2am Emergency Fix
TL;DR
Cause: Your application's redirect URI doesn't match Clerk's configured allowlist, or your Next.js middleware authentication logic runs *after* the redirect attempt.
Fix: Add your exact callback URL to Clerk Dashboard → Advanced → Redirect URIs, and ensure clerkMiddleware() executes *before* any custom redirect logic.
---
Real Console Error Messages
Look for these exact errors in your browser console (F12 → Console tab):
``` 1. GET http://localhost:3000/auth/callback?code=abc123 302 Found Location: http://localhost:3000/auth/callback (redirect loop detected)
2. Error: Invalid redirect_uri. The URI "http://myapp.com/auth/callback" is not whitelisted in your Clerk application settings.
3. clerk.redirect() called with URI that failed allowlist validation
4. [Clerk] Unable to complete sign-in. Redirect URI mismatch: expected "https://yourdomain.com" but got "http://yourdomain.com"
5. Next.js middleware: rewrites redirected to /auth/callback infinitely because Clerk session not yet established ```
---
Broken Code vs. Exact Fix
Problem 1: Missing Redirect URI in Clerk Dashboard
❌ Broken (what happens) ```javascript // app/auth/callback/route.ts export async function GET(req: Request) { const { searchParams } = new URL(req.url); const code = searchParams.get('code'); // Clerk tries to redirect here, but this URI isn't allowlisted // Result: 302 → 302 → 302 (infinite loop) } ```
✅ Fixed
1. Go to [Clerk Dashboard](https://dashboard.clerk.com)
2. Select your application
3. Navigate to Settings → Advanced → Redirect URIs
4. Add exact URIs:
- http://localhost:3000 (development)
- https://yourdomain.com (production)
- https://yourdomain.com/auth/callback (if using custom callback)
5. Save and wait 30 seconds for cache invalidation
Problem 2: Middleware Ordering (Next.js)
❌ Broken ```typescript // middleware.ts (WRONG ORDER) import { NextResponse } from 'next/server'; import { clerkMiddleware } from '@clerk/nextjs/server';
export default function middleware(request: Request) { // ❌ Custom logic runs FIRST, before Clerk validates session if (!request.nextUrl.pathname.startsWith('/api')) { // Clerk session doesn't exist yet—redirect triggers return NextResponse.redirect(new URL('/auth/signin', request.url)); } // ❌ Clerk runs AFTER redirect already happened return clerkMiddleware()(request); }
export const config = { matcher: ['/((?!.*\\..*|_next).*)', '/'], }; ```
✅ Fixed ```typescript // middleware.ts (CORRECT ORDER) import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'; import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server';
const isProtectedRoute = createRouteMatcher(['/dashboard(.*)']);
export default clerkMiddleware((auth, request) => { // ✅ Clerk middleware wraps everything // ✅ Session is NOW validated before your logic runs const { userId } = auth(); // Now safe to check authentication if (isProtectedRoute(request) && !userId) { return NextResponse.redirect( new URL('/sign-in?redirect_url=' + request.nextUrl.pathname, request.url) ); } });
export const config = { matcher: ['/((?!.*\\..*|_next).*)', '/'], }; ```
Problem 3: Protocol Mismatch (HTTP vs HTTPS)
❌ Broken ``` Clerk Dashboard shows: https://yourdomain.com Your app redirects from: http://yourdomain.com → Mismatch = redirect loop ```
✅ Fixed ```typescript // next.config.js or vercel.json // Force HTTPS in production headers: { 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains' } ```
And in Clerk Dashboard, use only HTTPS for production URLs.
---
Still Broken? Check These Too
1. Cookie domain mismatch: If using yourdomain.com but accessing www.yourdomain.com, Clerk can't read the session cookie. Solution: Add both variants to Redirect URIs, or use wildcard domain in cookies if your Clerk plan supports it. *(Uncertain: wildcard behavior varies by Clerk version—check [official docs](https://clerk.com/docs))*
2. Stale build cache: Your Next.js build cached old middleware. Run rm -rf .next && npm run build and redeploy.
3. Custom auth wrapper around clerkMiddleware: If you wrapped clerkMiddleware() in another function, execution order breaks. Remove wrappers and call it directly. See [middleware integration guide](/?guide=clerk-middleware).
---
Quick Checklist
clerkMiddleware() is the outermost wrapper in middleware.tshttps:// not http:// in production.next build folder---
Still Seeing the Loop?
Enable debug logging:
```typescript // app/layout.tsx import { ClerkProvider } from '@clerk/nextjs';
export default function RootLayout({ children }) { return ( <ClerkProvider debug={process.env.NODE_ENV === 'development'} > {children} </ClerkProvider> ); } ```
Check Network tab → filter by callback → see the exact redirect chain. Copy the full URL and verify it matches Clerk allowlist *exactly*.
Official Clerk Docs: https://clerk.com/docs/authentication/redirects
Related: [Clerk session validation errors](/?guide=clerk-session) | [Next.js middleware debugging](/?guide=nextjs-middleware)
---
Found a different variation of this redirect loop? Drop it in the comments—2am bugs deserve 2am fixes.