I am building a SaaS app with Next.js 14 (App Router) and Clerk (latest version). I am encountering a critical desynchronization issue between the Client and the Server (Middleware).
My application is stuck in a state where:
The Frontend (Client) is successfully authenticated. The Clerk cookie is present, and
useUser()returnsisSignedIn: true.The Backend (Middleware) fails to detect the session.
auth()returnsuserId: nullandisAuthenticated: false.
Because the Middleware thinks the user is anonymous, it either redirects them back to sign-in (causing a loop) or fails to redirect them to the dashboard.
My Code Setup:
Middleware (src/proxy.ts):
import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";
import { NextResponse } from "next/server";
const isPublicRoute = createRouteMatcher([
"/", "/sign-in(.*)", "/sign-up(.*)", "/api/webhooks(.*)",
]);
export default clerkMiddleware(async (auth, req) => {
const { userId } = await auth(); // <--- THIS RETURNS NULL
const url = req.nextUrl;
console.log("🔥 Middleware running on:", url.pathname);
console.log("👤 User ID:", userId);
// Logic: If user is logged in and on sign-in page, go to dashboard
if (userId && url.pathname.startsWith("/sign-in")) {
return NextResponse.redirect(new URL("/dashboard", req.url));
}
if (!isPublicRoute(req) && !userId) {
await auth.protect();
}
return NextResponse.next();
});
export const config = {
matcher: ['/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)', '/(api|trpc)(.*)'],
};
What I have tried:
Verified that
CLERK_SECRET_KEYmatches the project in Clerk Dashboard.Cleared all browser cookies and local storage.
Verified that middleware is actually running (logs appear in terminal).
Why is auth() in the middleware unable to read the session/cookie that is clearly present and valid on the client side? Is this related to how Next.js loads environment variables in the middleware runtime, or a Clerk v5 specific configuration, or something? I need help so bad