Deploying React AppsEntry 9 of 11

Server Rewrites for Client-Side Routing

A server rule that returns index.html for every URL that isn't a real file, so the client-side router can handle it.

Notes

  • In an SPA, /events/123 doesn't exist as a file on the server. Clicking a <Link> works because React Router handles it in the browser.
  • But refreshing or opening /events/123 directly sends that path to the server, which returns 404.
  • The fix: rewrite all unknown paths to /index.html. The app loads, and the router reads the URL and renders the right page.
  • Firebase's "Configure as a single-page app" option adds this automatically.
  • Real files (JS, CSS, images) are still served normally because rewrites only apply when no file matches.

What / Why / When

What

A catch-all fallback to index.html.

Why

Deep links and refreshes work.

When

Every SPA using browser history routing (React Router's createBrowserRouter, Angular's default router).

Config examples

// firebase.json{  "hosting": {    "public": "dist",    "ignore": ["firebase.json", "**/.*", "**/node_modules/**"],    "rewrites": [{ "source": "**", "destination": "/index.html" }]  }}
# Netlify: public/_redirects/*  /index.html  200
// vercel.json{ "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }] }
# Nginxlocation / {  try_files $uri $uri/ /index.html;}

Angular

Identical requirement and identical configs. Only the public directory changes (dist/<project>/browser).

Differences and gotchas

  • Hash routing (createHashRouter, Angular's withHashLocation()) avoids rewrites (/#/events/123) but produces uglier URLs. Use it only when you can't configure the server.

Quick reference

HostRewrite
Firebase"rewrites": [{ "source": "**", "destination": "/index.html" }]
Netlify/* /index.html 200
Vercelrewrites in vercel.json
Nginxtry_files $uri $uri/ /index.html;