Rewrite to index.html ignored for React + Vite SPA (404 on routes)

Hi all,

I’m deploying a React + Vite SPA to Vercel (not using Next.js), and I’m seeing Vercel’s default 404 page on client-side routes like /profile, even though the root (/) loads correctly.

What I’ve Tried:

  • Rewrite to /index.html in vercel.json
  • Ensured vite.config.js includes base: '/'
  • Removed framework field from vercel.json
  • Set project preset to "Other" in Vercel
  • Cleared cache and redeployed multiple times
  • Confirmed the route works locally with npx serve dist
  • index.html is present and served for root /

What’s Happening:

  • SPA routes like /profile return Vercel’s generic 404
  • Vercel appears to ignore the rewrite rule in vercel.json
  • The app never loads → React Router never takes over

I believe this may be a deeper issue with how rewrites are applied to non-Next.js frameworks, or potentially a conflict with internal defaults.

Appreciate any help or confirmation from the Vercel team :folded_hands:


vercel.json:

{
“version”: 2,
“buildCommand”: “npm run build”,
“outputDirectory”: “dist”,
“rewrites”: [
{
“source”: “/(.)",
“destination”: “/index.html”
}
],
“headers”: [
{
“source”: "/(.
)”,
“headers”: [
{ “key”: “X-Content-Type-Options”, “value”: “nosniff” },
{ “key”: “X-Frame-Options”, “value”: “DENY” },
{ “key”: “Strict-Transport-Security”, “value”: “max-age=31536000; includeSubDomains” }
]
}
],
“cleanUrls”: true,
“trailingSlash”: false
}


vite.config.js:

export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), “”);
return {
base: “/”,
build: { outDir: “dist” },
plugins: [react()],
server: { port: 8080 },
};
});

There’s another community post with 404 debugging tips that might be helpful. Please give these solutions a try and let us know how it goes.

A human should be around soon to offer more advice. But you can also get helpful information quickly by asking v0.

1 Like

Hey, welcome to the Vercel community!

This should generally work out of the box, so it’s hard to say exactly where you’re going wrong. If you can share your repo or a minimal reproduction I can help further

Here’s a fresh example repo of running a Vite + React Router 7 SPA (using the plain react vite plugin), with demo link available

Good morning Jacob, thank you so much for reaching out. We’re a bit baffled over here! Here’s the repo in question: https://github.com/marissaCopp/sleek-screen-simplified

I’ve run this past a few people who couldn’t identify any obvious issues, and though I found topics posted in the community describing the exact same issue, none of them had solutions posted.

Thank you in advance for any help or guidance you can provide. If you need any more information from me, please let me know!

Kind regards,

Marissa

1 Like

Hi Marissa, you’re right this was a baffling one!

The issue was that cleanUrls: true removes the .html from paths, so your destination rewrite needs to also remove it. I’ll reach out internally and see if we can get this documented

Either of these should work

With cleanUrls removed

{
  "$schema": "https://openapi.vercel.sh/vercel.json",
  "rewrites": [
    {
      "source": "/(.*)",
      "destination": "/index.html"
    }
  ],
  "regions": [
    "iad1"
  ],
  "trailingSlash": false,
  "git": {
    "deploymentEnabled": {
      "main": true
    }
  }
}

With cleanUrls enabled

{
  "$schema": "https://openapi.vercel.sh/vercel.json",
  "rewrites": [
    {
      "source": "/(.*)",
      "destination": "/index"
    }
  ],
  "regions": [
    "iad1"
  ],
  "cleanUrls": true,
  "trailingSlash": false,
  "git": {
    "deploymentEnabled": {
      "main": true
    }
  }
}
1 Like

Nailed it! You’re a hero!

Setting “cleanUrls” to “false” did the trick.

Thanks a million

2 Likes

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.