Next.js + Clerk: Middleware auth().userId is null while Client useUser() is fully authenticated (Session desync)
11:21 11 Jan 2026

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:

  1. The Frontend (Client) is successfully authenticated. The Clerk cookie is present, and useUser() returns isSignedIn: true.

  2. The Backend (Middleware) fails to detect the session. auth() returns userId: null and isAuthenticated: 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:

  1. Verified that CLERK_SECRET_KEY matches the project in Clerk Dashboard.

  2. Cleared all browser cookies and local storage.

  3. 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

next.js clerk