I’m experiencing an issue where all dynamic API routes in my Next.js App Router project return 404 on Vercel, despite building successfully and appearing in the Functions list.
Environment
Next.js version: 15.4.7
Router: App Router
Production URL:https://paideia-sigma.vercel.app
Framework detected: Next.js (auto-detected after removing vercel.json)
Local development: Works perfectly with npm run dev
Current behavior: On Vercel, all dynamic API routes (/api/[param]), e.g. /api/quiz/4w4EHpNCtGXv0CDCM52T and /api/test-dynamic/123, return the static 404 page (X-Matched-Path: /404) with no function logs. Static routes like /api/health and /api/test-simple work.
Expected behavior: Dynamic API routes should match their functions (they’re listed in Functions) and return JSON.
The pattern is clear: routes without dynamic segments [param] work fine, but any route with a dynamic segment returns 404.
Steps to reproduce:
Hit https://paideia-sigma.vercel.app/api/health → 200 JSON.
Hit https://paideia-sigma.vercel.app/api/test-dynamic/123 → 404 page, X-Matched-Path: /404, no logs.
Same for /api/quiz/.
Key observations:
Routes work perfectly locally with npm run dev
Routes appear in the Functions list in Vercel dashboard
Build logs show successful compilation
Response headers show X-Matched-Path: /404
No function execution logs are generated when accessing dynamic routes on Vercel
Removed vercel.json - no change
Middleware only matches /admin/*
Why would all dynamic API routes return 404 while static routes work, despite successful builds and the routes appearing in the Functions list? Is this a known issue with Next.js 15.4.7 or is there a configuration problem I’m missing?
and ofc the routes work perfectly locally with npm run dev. Let me know if there are any questions, I vibe coded this so I will try my best to answer. This is also my first deployment so I’m learning as much as I can.
Yes, but in my app I’m accessing quizzes by ID via /api/quiz/[id]. For example: /api/quiz/4w4EHpNCtGXv0CDCM52T.
I should’ve specified, I’m not trying to match /api/quiz without an ID, only /api/quiz/[id]. My problem is even though there are no segments after id, it still doesn’t work with just /api/quiz/[id] and it’s driving me crazy.
Locally, hitting /api/quiz/4w4EHpNCtGXv0CDCM52T works; on Vercel it returns the static 404 page (X-Matched-Path: /404) with no function logs, even though this route is listed in Functions.
I tried something new:
The App Router handler is a catch‑all at the end of the path: src/app/api/quiz/[…segments]/route.ts with:
I’m not hitting /api/quiz without an ID; only /api/quiz/{id}. Static APIs like api/health work; any dynamic API (/api/quiz/{id}) 404s the same way.
Given the catch‑all is at the end of the path and the route exists in Functions, why is router resolving /api/quiz/ to /404 on this deployment?? Thank you