fix guide
404 on refresh on Vercel
Harjot · · updated
You open your site and click through to an inner page like /dashboard. It works. Then you refresh, or paste that address into a new tab, and Vercel shows 404: NOT_FOUND.
The app is usually fine. On a refresh the browser asks Vercel for a file at that address, and Vercel has none to give. Check these five in order. Each takes a few minutes in a dashboard or one small file. Built with v0 or another Next.js tool? The Next.js causes are in /guides/v0-app-404-on-refresh.
1. A rewrite to index.html (Vite, React Router, Lovable, Bolt)
Apps from Lovable and Bolt are usually Vite with React Router. The whole site is one file, index.html, and the router draws each page in the browser. Clicking a link works because the router handles it. A refresh asks Vercel for /dashboard, Vercel has no file with that name, and it answers 404.
The fix is a file named vercel.json in the project root, next to package.json: { "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }] }. It tells Vercel to send every address to index.html and let the router take over. Commit it and redeploy.
On Netlify the same fix is a file named _redirects in the public folder, with one line: /* /index.html 200.
signs Clicking around works. Refreshing any page except the home page shows 404, and so does opening a shared link.
2. Framework preset and output directory
In Vercel, open the project, then Settings, then Build and Deployment. The Framework Preset should match your app: Vite for a Vite app, Next.js for a Next.js app. If it says Other, Vercel has no idea how to serve what the build made.
Output Directory is the folder the build produces. Vite builds into dist. A wrong folder deploys an empty site, so every address returns 404, the home page included.
A Next.js app that Vercel detects as Next.js needs no Output Directory. If someone set one by hand, remove it. An app set to static export (output: "export" in next.config) writes to a folder called out instead.
signs Every address gives 404, home page included, right after a deploy that says Ready.
3. Root Directory in a monorepo
If the repo holds more than one app, Vercel has to know which folder is yours. Under Settings, then General, check Root Directory. It should be the folder with your app's package.json, for example apps/web. If it's blank, Vercel builds from the top of the repo and may pick the wrong app or none.
After you change it, redeploy. Old deployments keep the settings they were built with.
signs The build log shows a different app than yours, or the site returns 404 on every address.
4. File name capitals and trailing slashes
Your Mac treats About.tsx and about.tsx as the same file. Vercel builds on Linux, where they are two files. A page or image that works on your machine can then be missing online, or the build fails with "Module not found". Make the name in the code match the name on disk, letter for letter. Git on a Mac can ignore a rename that only changes capitals, so use git mv.
In Next.js, the trailingSlash setting in next.config decides whether the address is /about or /about/. If you switched it recently and old links or a sitemap use the other form, test them.
signs Only some pages or images give 404, and they work on your computer. The build log says "Module not found" with a file name in different capitals.
5. What Vercel actually deployed
Open the deployment in Vercel and open its Source tab, then switch to Output to see the files the build produced. A Vite app should show index.html. If it's missing, the build wrote to a different folder than the Output Directory from step 2, or the Root Directory from step 3 points at the wrong place.
Then open the deployment's own vercel.app address. If it works there and your own domain shows 404, check under Domains that the domain points at your latest production deployment.
signs index.html isn't in the file list. Or the vercel.app address works and your domain shows 404.
Test it like a new user
- Open the live site in a private window.
- Click through to an inner page, like /dashboard.
- Refresh the page and check it still loads.
- Paste that inner address into a new tab.
- Send the address to a friend and ask them to open it on their phone.
Common questions
Why does my Vercel site show 404 NOT_FOUND when I refresh an inner page?
Vercel answers 404 because a Vite or React Router app has one real file, index.html, and the router draws each page. Add a vercel.json file in the project root with a rewrite that sends every address to index.html. Commit it and redeploy.
Why does every page on my Vercel site show 404, even the home page?
A wrong setting in Build and Deployment causes it. The Framework Preset should match your app, and Output Directory must be the folder the build produces, such as dist for Vite. A wrong folder deploys an empty site, so every address returns 404.
Why does my monorepo on Vercel show 404 or deploy the wrong app?
Vercel builds from the top of the repo when Root Directory is blank. Under Settings, then General, set Root Directory to the folder with your app's package.json, such as apps/web. Then redeploy, because old deployments keep the settings they were built with.
Still stuck?
Not sure this is the cause? Paste your app's link into the free scan. It lists what a visitor's browser can see, like leaked keys and exposed files, worst first. The score is free; every fix as a prompt for your builder is $9, one-time.
Want a person to look inside? See the diagnosis