Clerk: redirect loop after authentication [2026 fix]

Redirect loop after Clerk auth caused by missing middleware or incorrect callback URL config. Fix: verify middleware order and exact redirect URI match.

Clerk: Redirect Loop After Authentication - Emergency Fix

TL;DR

Cause: Clerk's auth callback URL doesn't exactly match your middleware redirect configuration or middleware is processing auth routes before Clerk's auth handler. Fix: Ensure your signInUrl, signUpUrl, and callback URLs match precisely (including trailing slashes and protocol), and place Clerk middleware before custom redirects.

---

Real Console Error Messages

Here are exact errors you'll see in production:

``` 1. ERR_TOO_MANY_REDIRECTS: net::ERR_TOO_MANY_REDIRECTS at https://yourdomain.com/sign-in

2. Uncaught Error: Clerk: redirect loop detected in middleware at ClerkMiddleware.process (clerk@4.x.x)

3. warning: Redirect loop detected. Redirecting from /sign-in to /callback which redirects back to /sign-in context: authentication

4. [clerk] Invalid callback URL. Expected: https://yourdomain.com/auth/callback but received: http://yourdomain.com/auth/callback (protocol mismatch)

5. Infinite redirect: https://yourdomain.com/sign-in -> https://yourdomain.com -> https://yourdomain.com/sign-in Middleware chain interrupted at step 3 ```

---

Broken Code vs. Exact Fix

Problem 1: Callback URL Mismatch

BROKEN: ```javascript // middleware.ts import { clerkMiddleware } from '@clerk/nextjs/server';

export default clerkMiddleware();

// clerk.json (or env var) CLERK_SIGN_IN_URL=/sign-in CLERK_REDIRECT_URL=https://yourdomain.com/dashboard // Missing /auth/callback ```

FIXED: ```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 CLERK_SIGN_IN_URL=/sign-in CLERK_SIGN_UP_URL=/sign-up CLERK_AFTER_SIGN_IN_URL=/dashboard CLERK_AFTER_SIGN_UP_URL=/dashboard

Callback URL configured in Clerk Dashboard EXACTLY matching your domain+protocol

```

Problem 2: Middleware Chain Order

BROKEN: ```javascript // next.config.js or middleware.ts const middleware = [ customRedirectMiddleware, // Runs first - redirects /sign-in to / clerkMiddleware(), // Never gets to process auth ]; ```

FIXED: ```javascript // middleware.ts import { clerkMiddleware } from '@clerk/nextjs/server'; import type { NextRequest } from 'next/server'; import { NextResponse } from 'next/server';

export default clerkMiddleware(async (auth, req: NextRequest) => { // Clerk processes FIRST - before any custom logic const { userId } = await auth(); // Custom redirects happen AFTER Clerk middleware if (!userId && !req.nextUrl.pathname.startsWith('/sign-in')) { return NextResponse.redirect(new URL('/sign-in', req.url)); } return NextResponse.next(); });

export const config = { matcher: ['/((?!_next|.*\\..*|favicon).*)'], }; ```

Problem 3: Protocol/Domain Mismatch

BROKEN: ``` Clerk Dashboard Callback URL: https://yourdomain.com/auth/callback ENV Variable: http://yourdomain.com/auth/callback (http vs https) ```

FIXED: ```bash

.env.local - match EXACTLY what's in Clerk Dashboard

CLERK_PUBLISHABLE_KEY=pk_test_xxxxx CLERK_SECRET_KEY=sk_test_xxxxx NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/dashboard NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/dashboard

Production only:

NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_xxxxx CLERK_SECRET_KEY=sk_live_xxxxx ```

Then in Clerk Dashboard (Settings > API Keys > Authorized redirect URIs):

  • Add: https://yourdomain.com/auth/callback (HTTPS, no trailing slash)
  • Dev: http://localhost:3000/auth/callback
  • ---

    Still Broken? Check These Too

    1. Trailing slash mismatch - /sign-in vs /sign-in/ will cause loops. Remove trailing slashes from all Clerk route configs.

    2. Multiple Clerk instances - If you have multiple <ClerkProvider> wrappers or call clerkMiddleware() twice, it creates duplicate auth processing. Search your codebase for duplicate imports.

    3. ISP/Proxy stripping HTTPS - Some corporate proxies downgrade HTTPS to HTTP. Verify Clerk Dashboard callback URL uses matching protocol: check your server logs with console.log(req.headers['x-forwarded-proto']).

    ---

    Verification Checklist (90 seconds)

  • [ ] Clerk Dashboard callback URL matches your domain exactly (including protocol)
  • [ ] Middleware runs BEFORE custom redirect logic
  • [ ] No trailing slashes on /sign-in or /sign-up config
  • [ ] CLERK_SIGN_IN_URL env var matches your actual sign-in route
  • [ ] Browser dev tools: Network tab shows no circular redirects (should see 302 → callback → dashboard)
  • ---

    Need more context? See our [middleware configuration guide](/?guide=clerk-middleware) and [auth setup troubleshooting](/?guide=clerk-setup).

    Official Clerk docs: [Clerk Middleware Reference](https://clerk.com/docs/references/nextjs/clerk-middleware) and [Redirect URL Configuration](https://clerk.com/docs/deployments/clerk-urls)

    Found a different variation? Drop it in the comments—2am debugging is a community effort.

    🔥 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