Clerk: redirect loop after authentication [2026 fix]
Redirect loop caused by missing/mismatched redirect URI in Clerk dashboard. Fix: add exact callback URL to Allowed redirect URLs.
TL;DR
Cause: Your redirectUrl or callback URI doesn't match the allowed redirect URIs configured in your Clerk dashboard.
Fix: Add your exact redirect URL (including protocol and port) to "Allowed redirect URLs" in Clerk Dashboard → Applications → Settings.
---
Exact Error Messages
Here are the console outputs you'll see during a redirect loop:
``` 1. [Clerk] Unhandled rejection: Error: Redirect URI mismatch. Received: http://localhost:3000/auth/callback Allowed: http://localhost:3000/
2. Error: The redirect_uri provided does not match the ones configured for your application
3. [nextjs-clerk] Invalid redirect configuration at middleware level
4. Clerk: Redirect loop detected. Check your NEXT_PUBLIC_CLERK_REDIRECT_URL environment variable
5. Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://[your-domain].clerk.accounts.dev. (Reason: CORS request did not succeed) ```
---
Broken vs. Fixed Code
Next.js + Clerk (Most Common)
BROKEN:
```javascript // middleware.ts import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
const isPublicRoute = createRouteMatcher([ '/', '/sign-in', '/sign-up', ]);
export default clerkMiddleware((auth, req) => { if (!isPublicRoute(req)) { auth().protect(); } });
// .env.local (MISSING or WRONG) NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_... CLERK_SECRET_KEY=sk_live_... // ❌ Missing NEXT_PUBLIC_CLERK_REDIRECT_URL ```
FIXED:
```javascript // middleware.ts (same code - issue was config) import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
const isPublicRoute = createRouteMatcher([ '/', '/sign-in', '/sign-up', '/auth/callback', // ✅ Add callback route explicitly ]);
export default clerkMiddleware((auth, req) => { if (!isPublicRoute(req)) { auth().protect(); } });
// .env.local (CORRECTED) NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_... CLERK_SECRET_KEY=sk_live_... NEXT_PUBLIC_CLERK_REDIRECT_URL=http://localhost:3000 // OR for production: NEXT_PUBLIC_CLERK_REDIRECT_URL=https://yourdomain.com ```
React + Clerk
BROKEN:
```javascript // main.jsx import { ClerkProvider } from '@clerk/clerk-react';
const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY;
ReactDOM.createRoot(document.getElementById('root')).render( <ClerkProvider publishableKey={clerkPubKey}> {/* ❌ No redirectUrl specified */} <App /> </ClerkProvider> ); ```
FIXED:
```javascript // main.jsx import { ClerkProvider } from '@clerk/clerk-react';
const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY;
ReactDOM.createRoot(document.getElementById('root')).render( <ClerkProvider publishableKey={clerkPubKey} // ✅ Specify exact post-auth redirect afterSignInUrl="/dashboard" afterSignUpUrl="/onboarding" > <App /> </ClerkProvider> );
// .env.local VITE_CLERK_PUBLISHABLE_KEY=pk_live_... // In Clerk Dashboard, add to Allowed redirect URLs: // http://localhost:5173 (for dev) // https://yourdomain.com (for production) ```
---
Dashboard Configuration (Critical)
1. Go to dashboard.clerk.com → Select your application
2. Navigate to Settings → URLs
3. Under "Allowed redirect URLs", add:
- http://localhost:3000 (development)
- http://localhost:3000/auth/callback (if using callback route)
- https://yourdomain.com (production)
- https://yourdomain.com/auth/callback (if applicable)
4. Save changes
5. Wait 30 seconds for DNS propagation
6. Hard refresh browser (Ctrl+Shift+R or Cmd+Shift+R)
---
Still Broken? Check These Too
1. Port Mismatch: If your app runs on :3001 but you configured :3000, the entire URL won't match. Update both .env and dashboard to use the same port.
2. Protocol Mismatch: http://localhost ≠ https://localhost. Check your environment and configure both the code AND dashboard identically.
3. Trailing Slashes: Some configurations treat http://localhost:3000/ (with slash) differently from http://localhost:3000 (no slash). Test both in your dashboard's allowed URLs.
4. Session Token Expiration: If you've been debugging for 20+ minutes, your session may have timed out. Sign out completely, clear cookies, and try fresh.
5. Stale Environment Cache: If using Docker or a build tool, your .env changes may not reload. Restart the dev server entirely: npm run dev (fresh process).
---
Quick Checklist
.env file has NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY and CLERK_SECRET_KEY.env matches exactly (protocol + domain + port)---
Additional Resources
---
Found a different variation? Drop it in the comments—this guide gets updated with community fixes.