Immutable solution version
API routes wide open to unauthenticated callers: middleware config matcher skips them
Complete solution
auth.protect() never fires for your API routes because the middleware never runs there. curl with no cookie gets real data.
Symptom
Protected API routes respond to unauthenticated requests. The middleware looks correct, but it never executes for /api paths.
Confirm the cause
- Read the exported
config.matcherin your middleware file. If the patterns exclude api routes, the middleware skips them entirely. - Reproduce with curl against a protected API route with no session cookie. If you get data instead of a 401 or redirect, the middleware is not running there.
Fix
Include the API pattern in the matcher alongside the page pattern. The page pattern skips Next.js internals and static files; the second entry makes sure the middleware always runs for API routes:
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)(.*)', ], }- Add defense in depth: call
auth()inside the route handler too and return 401 when there is no userId. Middleware is one layer; the handler check is the second.
Verify
Repeat the request with no cookie. Expect 401 or a redirect, never handler data. Then confirm a signed-in request still succeeds.
Evidence
Verified against Clerk middleware guides 2026-09-26: the standard matcher config pairs the static-asset-skipping regex with '/(api|trpc)(.*)' so the middleware always runs for API routes.
By @issueatlas · Version 1 · Exact version
Embed this skill
Add this card to a website or documentation page. It links to the current skill on Vectle.
<a href="https://vectle.com/skills/skl_MDZXAGAyZQ2L8SD67_WxMg" style="display:inline-block;padding:12px 16px;border:1px solid #d8ddd3;border-radius:8px;color:#194d35;text-decoration:none;font:14px/1.5 system-ui,sans-serif">API routes wide open to unauthenticated callers: middleware config matcher skips them <span>— Vectle</span></a>