Fix deep-link 404s on Vercel.

A single-page React app handles routes in the browser, so the server has no file for a path like /contact and returns a 404 when someone opens it directly. Add a rewrite that serves index.html for every unknown path.

Build and deploy · 5 min read · Updated 2026-10-10

Why it happens

Clicking a link inside the app works because React Router changes the URL without asking the server for anything. Opening the same URL in a new tab, refreshing it, or arriving from Google asks the server for /contact, and there is no file at that path in the build output.

The fix

Add a catch-all rewrite to vercel.json so any path without a matching file is served the app shell, which then renders the right route.

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

Static files such as /sitemap.xml, /robots.txt and /assets/* keep working, because Vercel serves files that exist before it applies rewrites.

The setting that silently breaks it

When we moved this site to Vercel, deep links still returned 404 with the rewrite in place. The cause was cleanUrls: true in the same file. Clean URLs changes how paths ending in .html are resolved, so the rewrite to /index.html never matched. Removing cleanUrls fixed every route at once.

A single-page app has no .html routes for cleanUrls to tidy, so there is nothing to lose by removing it.

Test it properly

Test with requests to inner URLs, not by clicking around the site, because in-app navigation never hits the server.

for path in / /services /blog/some-post /contact; do
  echo "$path $(curl -s -o /dev/null -w '%{http_code}' https://www.example.com$path)"
done

Every line should say 200. Then open one of those URLs in a fresh browser tab to confirm the right page renders.

One side effect to know about

With a catch-all rewrite, a URL that doesn't exist also returns 200 and shows your in-app 404 page. Search engines call this a soft 404. It's harmless for visitors, but the clean fix is to pre-render known routes and serve a real 404 for everything else. The last guide in this series covers that.

Questions

No. Vercel serves files that exist in the build output before applying rewrites, so static files are untouched.

Yes. Any client-side router that reads the URL in the browser needs the same server rewrite.

Because every path is served the app shell. Pre-rendering known routes and serving a real 404 for the rest fixes it.

Want it built for you?

We design, build and ship custom-coded sites with search built in from day one.