# Refreshing a page on Vercel or Netlify gives a 404? Your single-page app needs a rewrite

Clicking through the app is fine. Refreshing `/dashboard`, or opening it from a bookmark, isn't:

```plaintext
404: NOT_FOUND
Page not found
Cannot GET /dashboard
```

Routes like `/dashboard` only exist inside the JavaScript running in the browser. When the page is refreshed, the request goes to the host, the host looks for a file at that path, and there isn't one. So it answers 404 before the app gets a chance to load. Tell the host to serve `index.html` for app routes and the problem goes away.

This is one of the first things that breaks when a Lovable or Bolt app leaves the builder's hosting.

## Three usual causes

- **The host has no rewrite at all.** Lovable's own hosting does this automatically; Vercel, Netlify and storage buckets don't.
- **The rule is in a file the host never reads.** On Netlify, `_redirects` must land in the published folder. On Vercel, `vercel.json` must be at the project root.
- **The rule redirects instead of rewriting.** A 301 changes the URL or loops; you want a 200 rewrite.

## Confirm it

Paste a deep link such as `/dashboard` into a fresh tab. A host 404 that appears before any of your app loads means the rewrite is missing. Then look for `vercel.json` at the root, or `_redirects` in the deployed files.

## Vercel

At the project root, `vercel.json`:

```json
{
  "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
}
```

Redeploy.

## Netlify

Put the rule in `public/_redirects`, which Vite copies into the build output:

```plaintext
/*    /index.html   200
```

Redeploy. The trailing `200` is the part that makes it a rewrite.

## Don't lose real 404s

With the rewrite in place the host never says 404 again, so your app has to. Add a catch-all route that renders a "page not found" screen for addresses that don't exist.

## How to know it worked

- Several deep links, refreshed, each show the right screen.
- An address you made up shows your app's not-found screen.
- Images, `robots.txt` and `sitemap.xml` still load as files.

## Keeping it fixed

Commit the rule to the repository rather than setting it only in a dashboard, and test deep links on any new host before the domain points at it.

One note on SEO: the rewrite fixes the 404, not search. A client-rendered app gives crawlers little to read either way; pre-rendering key pages is the separate fix if search traffic matters.

Docs: [Vercel rewrites](https://vercel.com/docs/rewrites) and [Netlify rewrites and proxies](https://docs.netlify.com/routing/redirects/rewrites-proxies/).

The original guide is on our site: [Your app shows 404 when you refresh a page after deploying](https://stacksmesh.com/fix/spa-404-on-refresh-after-deploy?utm_source=hashnode&utm_medium=article&utm_campaign=BR-0003). Before a host move, the free [Launch Check](https://stacksmesh.com/tools/launch-check?utm_source=hashnode&utm_medium=article&utm_campaign=BR-0003) can catch other launch problems on the live app.
