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 (ApplicationCookies → 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