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
  • [ ] Redirect URL in .env matches exactly (protocol + domain + port)
  • [ ] Same URL is added to Clerk Dashboard → Settings → URLs
  • [ ] Browser cache cleared (Ctrl+Shift+Delete)
  • [ ] Dev server restarted after env changes
  • [ ] No typos in environment variable names
  • ---

    Additional Resources

  • [Clerk Middleware troubleshooting guide](/?guide=clerk-middleware)
  • [Environment variables reference](/?guide=clerk-env-vars)
  • [Official Clerk docs: Redirect URLs](https://clerk.com/docs/references/redirect-urls)
  • ---

    Found a different variation? Drop it in the comments—this guide gets updated with community fixes.

    🔥 0d
    LIVE
    ↓ PlanetScale rage spiking •⚠ Vercel pricing complaints •↑ Railway gaining fast •↑ Supabase happiness rising •↑ Resend loved by devs •↓ PlanetScale rage spiking •⚠ Vercel pricing complaints •↑ Railway gaining fast •↑ Supabase happiness rising
    DEVELOPER PAIN RADAR // Loading...

    Developers complain.
    Opportunities appear.

    We track what developers are struggling with today — and what opportunities that creates.

    —
    guides today
    —
    avg happiness
    🔥 Pain
    📖 Guides
    🔭 Explore
    👤 Mine
    🔥 Pain Radar — rage scores today
    ↗ share
    💡 Opportunity Feed — pain = market gap
    📈 Tool Momentum
    all scores →
    📖 Latest Guide
    all guides →
    📖 All Guides
    📊 Tool Scores
    + Submit
    📰 Hacker News
    ➕ Submit a Tool
    ← back