Clerk: redirect loop after authentication [2026 fix]

Clerk redirect loop caused by mismatched redirect URIs or missing CLERK_SIGN_IN_URL env var. Fix: update environment variables and Next.js middleware config.

Clerk: Redirect Loop After Authentication [2am Emergency Fix]

TL;DR

Cause: Your CLERK_SIGN_IN_URL environment variable doesn't match your actual sign-in route, or Clerk middleware is redirecting before checking authentication state. Fix: Set CLERK_SIGN_IN_URL=http://localhost:3000/sign-in (or production equivalent) and add publicRoutes to your middleware config.

---

Real Console Error Messages

``` Error: Redirect loop detected. Redirecting from /sign-in to /sign-in ```

``` UnhandledPromiseRejection: Error: The URL "undefined" is invalid. Check your CLERK_SIGN_IN_URL environment variable ```

``` Next.js redirect loop: ERR_TOO_MANY_REDIRECTS at /sign-in (HTTP 310) ```

``` Clerk: User is not authenticated, but redirect destination is also protected. This creates a loop. ```

``` TypeError: Cannot read property 'redirectUrl' of undefined in clerkMiddleware ```

---

The Problem: Side-by-Side Code Comparison

❌ BROKEN CODE

.env.local (missing or incomplete): ```env NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_abc123 CLERK_SECRET_KEY=sk_test_xyz789

Missing CLERK_SIGN_IN_URL!

```

middleware.ts (too restrictive): ```typescript import { clerkMiddleware } from '@clerk/nextjs/server';

export default clerkMiddleware();

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

✅ EXACT FIX

.env.local (corrected): ```env NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_abc123 CLERK_SECRET_KEY=sk_test_xyz789 CLERK_SIGN_IN_URL=/sign-in CLERK_AFTER_SIGN_IN_URL=/dashboard CLERK_AFTER_SIGN_OUT_URL=/ ```

middleware.ts (with publicRoutes): ```typescript import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';

const isProtectedRoute = createRouteMatcher(['/dashboard(.*)', '/api/protected(.*)']);

export default clerkMiddleware((auth, req) => { if (isProtectedRoute(req)) auth().protect(); });

export const config = { matcher: [ '/((?!_next|static|public|sign-in|sign-up).*)', ], }; ```

---

Why This Happens

Root cause chain: 1. User hits /dashboard (protected route) 2. Middleware sees no auth token → tries to redirect to sign-in URL 3. CLERK_SIGN_IN_URL is undefined → defaults to /sign-in 4. Middleware *also* protects /sign-in → redirects back to itself 5. Browser sees infinite redirect chain → error

OR:

1. CLERK_SIGN_IN_URL=/ (root) 2. User authenticates → redirects to / 3. Middleware checks / → sees it's protected → redirects to sign-in again 4. Loop persists

---

Critical Configuration Details

For Next.js 13.4+ (App Router):

  • Use /sign-in (relative paths work fine)
  • Ensure sign-in route is excluded from middleware matcher
  • publicRoutes handles this automatically in newer Clerk versions
  • For Next.js 12 or Pages Router:

  • Must use absolute URLs: CLERK_SIGN_IN_URL=https://yourdomain.com/sign-in
  • We can't confirm exact version behavior without your package.json—check your Clerk package version
  • ---

    Still Broken? Check These Too

    1. Stale cache + env vars not loaded: Kill your dev server (Ctrl+C), clear .next folder, restart npm run dev. Environment variables don't hot-reload.

    2. Clerk API rate limiting: If you're seeing "too many requests" in Network tab, you're stuck in a loop. Clear cookies (Application → Cookies → delete all __clerk_* entries), hard refresh (Ctrl+Shift+R).

    3. Conflicting auth libraries: If you have NextAuth.js or Auth0 middleware also active, they can fight Clerk's middleware. Disable competing auth in your middleware.

    4. Sign-in page doesn't exist: Create /app/sign-in/page.tsx with: ```typescript import { SignIn } from '@clerk/nextjs'; export default function SignInPage() { return <SignIn />; } ```

    5. Trailing slashes mismatch: CLERK_SIGN_IN_URL=/sign-in/ (with slash) won't match matcher for /sign-in (without). Remove trailing slashes.

    ---

    Verification Checklist

  • [ ] .env.local has all three variables: PUBLISHABLE_KEY, SECRET_KEY, SIGN_IN_URL
  • [ ] Dev server restarted after env changes
  • [ ] Cookies cleared in browser DevTools
  • [ ] /sign-in route is NOT in middleware's protected routes list
  • [ ] CLERK_SIGN_IN_URL value matches your actual sign-in page path
  • [ ] Sign-in page exists and renders <SignIn /> component
  • [ ] Clerk dashboard shows correct origin URLs (https://dashboard.clerk.com)
  • ---

    Related Issues

  • [Clerk session undefined after sign-in](/?guide=clerk-session-undefined)
  • [Next.js middleware execution order problems](/?guide=nextjs-middleware-order)
  • ---

    Official Resources

  • [Clerk Next.js Middleware Docs](https://clerk.com/docs/references/nextjs/clerk-middleware)
  • [Environment Variables Reference](https://clerk.com/docs/deployments/clerk-environment-variables)
  • ---

    Found a different variation? Drop it in the comments—variations include Remix/Express setups, custom domain issues, or multi-tenant configurations. Your 2am fix might save someone else's deploy.

    🔥 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