Clerk: redirect loop after authentication [2026 fix]
Clerk redirect loop caused by misconfigured CLERK_REDIRECT_URL or middleware order. Fix: set correct redirect URI in Clerk dashboard and reorder Next.js middleware.
Clerk: Redirect Loop After Authentication [2am Fix]
TL;DR
Cause: YourCLERK_REDIRECT_URL environment variable doesn't match your Clerk dashboard configuration, or your middleware is intercepting authenticated requests.
Fix: Update CLERK_REDIRECT_URL to match your Clerk instance's redirect URIs and ensure middleware allows authenticated users through.---
Exact Console Error Messages
``` Error: Invalid redirect_uri. The provided redirect_uri is not whitelisted. ```
``` [Clerk] The Clerk middleware encountered an error: Invalid redirect URI "http://localhost:3000/sign-in" - expected one of: ["http://localhost:3000"] ```
``` Infinite redirect loop detected. User authenticated but redirected back to /sign-in repeatedly. ```
``` (node:12845) UnhandledPromiseRejectionWarning: ClerkAPIResponseError: 403 Forbidden - redirect_uri not in allowed list ```
``` Warning: Clerk session created but middleware redirected to /sign-in before user reached protected route. ```
---
Broken Code vs. Fixed Code
Problem 1: Environment Variable Mismatch
BROKEN (.env.local): ```env CLERK_PUBLISHABLE_KEY=pk_test_... CLERK_SECRET_KEY=sk_test_... CLERK_REDIRECT_URL=http://localhost:3000/sign-in ```
FIXED (.env.local): ```env CLERK_PUBLISHABLE_KEY=pk_test_... CLERK_SECRET_KEY=sk_test_... CLERK_REDIRECT_URL=http://localhost:3000 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=/onboarding ```
Explanation: CLERK_REDIRECT_URL should be your base domain, NOT the sign-in path. The AFTER_SIGN_IN_URL controls where users go post-authentication.
---
Problem 2: Middleware Ordering
BROKEN (middleware.ts): ```typescript import { authMiddleware } from "@clerk/nextjs";
export const middleware = authMiddleware({ publicRoutes: ["/", "/pricing"], });
export const config = { matcher: ["/((?!.+\\.[\\w]+$|_next).*)", "/", "/(api|trpc)(.*)"], }; ```
FIXED (middleware.ts): ```typescript import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";
const isPublicRoute = createRouteMatcher([ "/", "/pricing", "/sign-in(.*)", "/sign-up(.*)", "/api/public(.*)", ]);
export const middleware = clerkMiddleware((auth, req) => { if (!isPublicRoute(req)) { auth().protect(); } });
export const config = { matcher: [ "/((?!.*\\..*|_next).*)", "/", "/(api|trpc)(.*)", ], }; ```
Explanation: Newer versions use clerkMiddleware with createRouteMatcher. Ensure sign-in/sign-up routes are explicitly marked as public to prevent the middleware from redirecting authenticated users back to sign-in.
---
Problem 3: Sign-In Page Configuration
BROKEN (app/sign-in/[[...index]]/page.tsx): ```typescript import { SignIn } from "@clerk/nextjs";
export default function SignInPage() { return <SignIn />; } ```
FIXED (app/sign-in/[[...index]]/page.tsx): ```typescript import { SignIn } from "@clerk/nextjs";
export default function SignInPage() { return ( <SignIn path="/sign-in" routing="path" signUpUrl="/sign-up" /> ); } ```
Explanation: Explicitly specify routing mode and URLs to prevent Clerk from using its default routing behavior, which may conflict with your middleware.
---
Verification Checklist
1. Clerk Dashboard: Navigate to Settings → API Keys. Verify CLERK_REDIRECT_URL value matches exactly what's in your .env.local.
2. Run locally: npm run dev and check browser console for specific 403 errors.
3. Clear cache: Hard-refresh (Cmd+Shift+R) and clear localhost cookies.
4. Check Next.js build: Run npm run build to catch configuration errors early.
---
Still Broken? Check These Too
1. [Clerk session not persisting across refreshes](/?guide=clerk-session-storage) — Verify your ClerkProvider wraps the entire app in layout.tsx.
2. [CORS errors with Clerk API calls](/?guide=clerk-cors-fix) — Ensure your domain is whitelisted in Clerk dashboard under Domains.
3. [Custom domain redirect loops](/?guide=clerk-custom-domain) — If using a custom domain, add it to Clerk settings AND your environment variables.
---
Version Notes
clerkMiddleware() and createRouteMatcher(). Older middleware syntax will cause loops.authMiddleware(). If you're on an older version, upgrade or consult the legacy docs.Not certain about your version? Run npm list @clerk/nextjs to verify.
---
Official Resources
---
Found a different variation? Drop it in the comments.