Clerk: redirect loop after authentication [2026 fix]
Redirect URI mismatch between Clerk dashboard and app code causes infinite loop. Fix: sync your NEXT_PUBLIC_CLERK_REDIRECT_URL with dashboard settings.
Clerk: Redirect Loop After Authentication [2am Fix]
TL;DR
Cause: Your redirect URI in Clerk dashboard doesn't match your app's environment variable or middleware configuration. Fix: SetNEXT_PUBLIC_CLERK_REDIRECT_URL to match your Clerk dashboard allowed redirect URIs exactly (including protocol and trailing slash).---
Real Console Error Messages
``` 1. Error: The redirect_uri parameter does not match any of the allowed redirect URIs configured for this application.
2. GET /api/auth/callback/clerk 307 redirect loop (repeated)
3. [clerk] Redirect URI mismatch: received "http://localhost:3000/" but expected "http://localhost:3000"
4. TypeError: Cannot read property 'afterSignInUrl' of undefined at ClerkProvider
5. Warning: useAuth hook called outside of <ClerkProvider> - redirecting to sign-in causes infinite loop ```
---
Broken Code vs. Exact Fix
Problem 1: Environment Variable Mismatch
BROKEN: ```javascript // .env.local NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_xxx CLERK_SECRET_KEY=sk_test_xxx // Redirect URL missing or incorrect
// pages/_app.tsx import { ClerkProvider } from '@clerk/nextjs';
export default function App({ Component, pageProps }) { return ( <ClerkProvider> <Component {...pageProps} /> </ClerkProvider> ); } ```
FIXED: ```javascript // .env.local NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_xxx CLERK_SECRET_KEY=sk_test_xxx NEXT_PUBLIC_CLERK_REDIRECT_URL=http://localhost:3000 // Must match dashboard exactly (no trailing slash for localhost)
// pages/_app.tsx import { ClerkProvider } from '@clerk/nextjs';
export default function App({ Component, pageProps }) { return ( <ClerkProvider afterSignInUrl="/dashboard" afterSignUpUrl="/dashboard" > <Component {...pageProps} /> </ClerkProvider> ); } ```
Problem 2: Middleware Not Configured
BROKEN: ```typescript // middleware.ts (missing or incomplete) // This allows unauthenticated access which breaks redirect flow export function middleware(req) { return NextResponse.next(); } ```
FIXED: ```typescript // middleware.ts import { authMiddleware } from '@clerk/nextjs';
export default authMiddleware({ publicRoutes: ['/', '/sign-in', '/sign-up'], ignoredRoutes: ['/api/webhooks'], });
export const config = { matcher: ['/((?!.+\\.[\\w]+$|_next).*)', '/', '/(api|trpc)(.*)'] }; ```
Problem 3: Dashboard Configuration Missing
BROKEN (Clerk Dashboard):
http://localhost:3000/http://localhost:3000 (no trailing slash)FIXED (Clerk Dashboard):
1. Go to [Clerk Dashboard](https://dashboard.clerk.com) → Applications → Select your app
2. Navigate to "API Keys" tab
3. Find "Allowed redirect URIs"
4. Add exactly what your .env.local has:
- Development: http://localhost:3000
- Production: https://yourdomain.com
5. Save and redeploy
---
Why This Happens
Clerk's authentication flow validates that your app redirects back to an approved URI for security. When your environment variable, middleware configuration, and Clerk dashboard settings don't align, the OAuth callback fails and tries to redirect again, creating an infinite loop.
Version note: This guide applies to @clerk/nextjs v4.31.0+. Earlier versions had different redirect handling—if you're on v4.x before .31, consider upgrading or check the legacy configuration below.
---
Still Broken? Check These Too
1. Cookie domain mismatch
If using a custom domain, ensure __session cookie domain matches. Check DevTools → Application → Cookies. The domain should be .yourdomain.com (with dot prefix for subdomains).
2. Race condition in useRouter()
Your useRouter().push() may fire before Clerk hydrates. Wrap redirects in useEffect with dependency on isLoaded from useAuth():
```typescript
const { isLoaded } = useAuth();
useEffect(() => {
if (isLoaded && !isSignedIn) router.push('/sign-in');
}, [isLoaded, isSignedIn]);
```
3. Multiple ClerkProvider instances
If you have <ClerkProvider> in both _app.tsx and _document.tsx, remove one. Only wrap at the app root.
---
Verification Checklist
.env.local has NEXT_PUBLIC_CLERK_REDIRECT_URL setmiddleware.ts imports authMiddleware from @clerk/nextjs<ClerkProvider>npm run dev)---
Related Resources
[Debugging auth loops in Next.js](/?guide=nextjs-auth-debugging) | [Environment variable setup walkthrough](/?guide=env-config)
Official Clerk Docs: [https://clerk.com/docs/nextjs/authenticate-with-nextjs](https://clerk.com/docs/nextjs/authenticate-with-nextjs)
---
Found a different variation? Drop it in the comments—this catches edge cases like Vercel preview deploys, regional CDNs, or custom OAuth flows.