Encountering 404 errors on page refresh with separate frontend and backend deployments

I have a project with a frontend built in React and a backend using Node.js, both deployed separately. While everything works when navigating the app, I run into 404 errors upon refreshing any page.

Here’s an overview of my setup:

  • Frontend: React application using Vite for building
  • Backend: API server developed with Express.js
  • Routing: Implemented through react-router-dom for client-side navigation
  • Both components are hosted separately on a web service.

Below is my basic Vite configuration:

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  server: {
    proxy: {
      '/api': {
        target: 'https://my-api-server.example.com',
        changeOrigin: true
      }
    }
  },
  plugins: [react()]
});

And this is how my Express server is set up:

import express from 'express';
import cors from 'cors';
import helmet from 'helmet';
import userRoutes from './routes/users.js';

const app = express();
app.use(cors());
app.use(express.json());
app.use(helmet());

app.use('/api', userRoutes);

if (process.env.NODE_ENV === 'production') {
  app.get('*', (req, res) => {
    res.sendFile(path.join(__dirname, '../frontend/build/index.html'));
  });
}

app.listen(process.env.PORT || 5000);

I suspect that the problem lies in the fallback routing, but I can’t quite pinpoint the issue. Any suggestions on how to resolve this?

Interesting setup! What are you using for hosting - Netlify or Vercel? Check if SPA fallback is enabled on your frontend platform. Does this happen on all routes or just nested ones like /users/profile?

for sure! u gotta add a catch-all route to serve index.html for unmatched paths. maybe check your host’s rewrite rules too, that should solve ur 404 problem!

This happens because you’ve got separate deployments for frontend and backend. When you refresh /dashboard, your frontend server gets the request but doesn’t have a server-side route for it. Since they’re deployed separately, your Express fallback route never kicks in for frontend routes. You need to configure your hosting provider to serve index.html for all non-asset requests. Most platforms let you do this with redirect rules or a _redirects file. Just create a _redirects file in your build output with /* /index.html 200. If you don’t set this up at the server level, direct URLs will always 404 because the server looks for actual files instead of letting React Router handle the routing.