reactjsazurebuildvite

Error 404 Not Found in deployed react.js + vite app in Azure App Service on page refresh


I have a React Vite app deployed in Azure the routes work well when clicking on links but when reloading the page in any of these routes, a 404 error shows. All routes work perfectly in dev local deployment after build, so I cannot recreate this in dev environment. I also added the _redirects file in the public folder, but still no change.
This is my vite.config.json

import { fileURLToPath, URL } from "node:url";

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

// https://vitejs.dev/config/
export default defineConfig({
  build: {
    commonjsOptions: {
      include: ["tailwind.config.js", "node_modules/**"],
    },
  },
  optimizeDeps: {
    include: ["tailwind-config"],
  },
  plugins: [react()],
  resolve: {
    alias: {
      "@": fileURLToPath(new URL("./src", import.meta.url)),
      "tailwind-config": fileURLToPath(
        new URL("./tailwind.config.js", import.meta.url)
      ),
    },
  },
});

The package.json file:

{
  "name": "tailwise-react",
  "version": "0.0.0",
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "@badeball/cypress-cucumber-preprocessor": "^20.0.2",
    "@ckeditor/ckeditor5-build-balloon": "^40.2.0",
    "@ckeditor/ckeditor5-build-balloon-block": "^40.2.0",
    "@ckeditor/ckeditor5-build-classic": "^40.2.0",
    "@ckeditor/ckeditor5-build-decoupled-document": "^40.2.0",
    "@ckeditor/ckeditor5-build-inline": "^40.2.0",
    "@cypress/webpack-preprocessor": "^6.0.1",
    "@fullcalendar/core": "^6.1.10",
    "@fullcalendar/daygrid": "^6.1.10",
    "@fullcalendar/interaction": "^6.1.10",
    "@fullcalendar/list": "^6.1.10",
    "@fullcalendar/react": "^6.1.10",
    "@fullcalendar/timegrid": "^6.1.10",
    "@headlessui/react": "1.7.18",
    "@hookform/resolvers": "^3.3.4",
    "@left4code/tw-starter": "^3.1.36",
    "@microsoft/signalr": "^8.0.7",
    "@reduxjs/toolkit": "^1.8.5",
    "axios": "^1.6.8",
    "chart.js": "^4.4.1",
    "cypress": "^13.7.1",
    "d3-force": "^3.0.0",
    "dayjs": "^1.11.10",
    "dropzone": "^6.0.0-beta.2",
    "elkjs": "^0.9.2",
    "esbuild": "^0.20.2",
    "flat": "^6.0.1",
    "highlight.js": "^11.9.0",
    "js-beautify": "^1.14.11",
    "leaflet": "^1.9.4",
    "leaflet.markercluster": "^1.5.3",
    "litepicker": "^2.0.12",
    "lucide": "^0.304.0",
    "lucide-react": "^0.307.0",
    "react": "^18.2.0",
    "react-beautiful-dnd": "^13.1.1",
    "react-dnd": "^16.0.1",
    "react-dnd-html5-backend": "^16.0.1",
    "react-dom": "^18.2.0",
    "react-draggable": "^4.4.6",
    "react-dropzone": "^14.2.3",
    "react-hook-form": "^7.49.3",
    "react-icons": "^5.0.1",
    "react-redux": "^9.0.4",
    "react-router-dom": "^6.21.1",
    "react-transition-group": "^4.4.5",
    "reactflow": "^11.10.4",
    "simplebar": "^6.2.5",
    "strip-ansi": "^7.1.0",
    "tabulator-tables": "^5.5.2",
    "tiny-slider": "^2.9.4",
    "tippy.js": "^6.3.7",
    "toastify-js": "^1.12.0",
    "tom-select": "2.3.1",
    "xlsx": "^0.18.5",
    "yup": "^1.3.3",
    "zoom-vanilla.js": "^2.0.6"
  },
  "devDependencies": {
    "@bahmutov/cypress-esbuild-preprocessor": "^2.2.0",
    "@esbuild-plugins/node-modules-polyfill": "^0.2.2",
    "@tailwindcss/forms": "^0.5.7",
    "@types/dropzone": "^5.7.8",
    "@types/flat": "^5.0.5",
    "@types/js-beautify": "^1.14.3",
    "@types/leaflet": "^1.9.8",
    "@types/leaflet.markercluster": "^1.5.4",
    "@types/lodash": "^4.14.202",
    "@types/node": "^20.10.6",
    "@types/react": "^18.2.47",
    "@types/react-beautiful-dnd": "^13.1.8",
    "@types/react-dom": "^18.2.18",
    "@types/react-transition-group": "^4.4.10",
    "@types/tabulator-tables": "^5.5.4",
    "@types/toastify-js": "^1.12.3",
    "@vitejs/plugin-react": "^4.2.1",
    "autoprefixer": "^10.4.16",
    "clsx": "^2.1.0",
    "lodash": "^4.17.21",
    "postcss": "^8.4.33",
    "postcss-advanced-variables": "^3.0.1",
    "postcss-import": "^16.0.0",
    "postcss-nesting": "^12.0.2",
    "tailwind-merge": "^2.2.0",
    "tailwindcss": "^3.4.1",
    "ts-loader": "^9.5.1",
    "typescript": "5.4.5",
    "vite": "^5.0.11",
    "webpack": "^5.91.0"
  },
  "resolutions": {
    "jackspeak": "2.1.1"
  },
  "cypress-cucumber-preprocessor": {
    "nonGlobalStepDefinitions": false,
    "stepDefinitions": "cypress/e2e/***.ts"
  }
}

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + React + TS</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

I'm using react-router-dom for the routes:

import { useRoutes } from "react-router-dom";

And my routes looks like this:

function Router() {
  const routes = [
    {
      path: "/",
      element: <Layout />,
      children: [
        {
          path: "dashboard-ooi-designer/:ooiId/:vertexType",
          element: <DashboardMain />,
        },
        {
          path: "cooi-designer",
          element: <TestCOOIModeler />,
        },
(...)

The main.tsx file:

import ScrollToTop from "@/components/Base/ScrollToTop";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import { Provider } from "react-redux";
import { store } from "./stores/store";
import Router from "./router";
import "./assets/css/app.css";
import { ReactFlowProvider } from "reactflow";
import ContextProvider from "./api/context";

ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
  <BrowserRouter>
    <ContextProvider>
      <Provider store={store}>
        <ReactFlowProvider>
          <Router />
        </ReactFlowProvider>
      </Provider>
      <ScrollToTop />
    </ContextProvider>
  </BrowserRouter>
);

If some of you have any ideas about this issue, I found some questions but no definitive fix.

I tried the _redirects file with this content in the public folder

/* /index.html 200

I also tried to replicate the behavior locally but it didn't happen.

This is the log in Azure when I reload the page:

enter image description here


Solution

  • I found the solution to the issue in my case, and I hope it can be useful to others. I was following this post: https://medium.com/@jolin1010/deploy-react-app-on-azure-with-custom-web-config-606e9f8cff07, that was a lot of more clarifying, but there is a small detail that makes the difference https://learn.microsoft.com/en-us/answers/questions/1660913/azure-app-service-wont-display-my-website

    Make sure you are using in your app service configuration, the start command:

    pm2 serve /home/site/wwwroot --no-daemon --spa