- Home page loads, but /dashboard or /login shows “Page not found” when refreshed or opened directly
- Netlify “Page not found”, Vercel 404: NOT_FOUND, or an Apache 404 on shared hosting
Why it happens
Single-page apps (React + Vite, React Router) have one real file — index.html. Routes like /dashboard exist only in JavaScript. When you refresh, the host looks for a file called /dashboard, doesn't find one and returns 404. You need a rewrite that serves index.html for unknown paths.
How to fix it
-
Add the fallback rule for your host
Pick your host below. Put the file in your project's public/ folder (Vite copies it into dist/) and redeploy.
-
Don't redirect real files
Use a rewrite (status 200), not a 301/302 redirect, and make sure it's the last rule so real assets like /assets/app.js still load.
Fallback rule for each host
Vercel
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
} Vercel docs (opens in a new tab)
Netlify
/* /index.html 200 Netlify docs (opens in a new tab)
Cloudflare Pages
# No config needed: if there is no top-level 404.html,
# Cloudflare Pages serves index.html for unknown routes.
# If you DO ship a 404.html, add public/_redirects with:
/* /index.html 200 Cloudflare Pages docs (opens in a new tab)
Railway
:{$PORT:3000} {
root * dist
encode gzip
file_server
try_files {path} /index.html
} Railway docs (opens in a new tab)
Render
# Dashboard rule: Source /* Destination /index.html Action Rewrite
routes:
- type: rewrite
source: /*
destination: /index.html Render docs (opens in a new tab)
Fly.io
location / {
try_files $uri $uri/ /index.html;
} Fly.io docs (opens in a new tab)
DigitalOcean App Platform
# Custom Pages → Catchall → Page Name: index.html DigitalOcean App Platform docs (opens in a new tab)
Hostinger
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L] Hostinger docs (opens in a new tab)
Replit Static deployment (.replit)
[[deployment.rewrites]]
from = "/*"
to = "/index.html"