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 buildlocally and reading.vercel/output/config.jsonreproduced the bug in seconds. output: 'export'is for hosting theout/folder somewhere else. If you deploy to Vercel anyway, you probably don't need it.