← Blog

Why my Next.js static export returned 404 on Vercel

I moved this site's deploys from Vercel's Git integration to GitHub Actions. The first production deploy succeeded, and then the home page returned a 404.

The symptom

/ returned 404. /index returned 200 and served the right page. The previous deployment, built by Vercel from the same next.config.mjs, served / just fine.

The pipeline

The workflow is the one Vercel documents: build on the GitHub runner, then upload the result.

vercel pull --yes --environment=production
vercel build --prod
vercel deploy --prebuilt --prod

The cause

The site had output: 'export' in next.config.mjs. When vercel build runs a project in that mode, Vercel's Next.js builder (@vercel/next) takes the static out/ directory and strips .html from every file, so index.html is served at the path index. The generated .vercel/output/config.json showed it:

"overrides": { "index.html": { "path": "index" } }

The routes had no entry mapping / to index, only a catch-all 404 after the filesystem check. So /index worked and / did not.

Export mode alone wasn't enough to break it: the older deployment had the same setting and worked. It took export mode built through the CLI and deployed with --prebuilt. I didn't find out why Vercel's own build took a different path.

The fix

I removed output: 'export'. On Vercel a static export buys little: the normal Next.js build still prerenders every page as static HTML, and in that output / is served from a prerendered index page. The trade-off is that export mode fails the build if anything server-side sneaks in; without it, that code quietly becomes a function. For a static site, checking the build's route table covers that.

Takeaways

  • Test the deploy path you will actually use. Running vercel build locally and reading .vercel/output/config.json reproduced the bug in seconds.
  • output: 'export' is for hosting the out/ folder somewhere else. If you deploy to Vercel anyway, you probably don't need it.