vite: Dots in URL lead to 404 (regression?)

⚠️ IMPORTANT ⚠️ Please do not ignore this template. If you do, your issue will be closed immediately.

Describe the bug

URLs with dots are leading to a 404 error instead of correctly displaying the corresponding page.

Note that it works fine with a project created with Vue CLI but it doesn’t work with a project created with @vite/create-app (both using Vue 3 with Vue Router 4).

I guess this is a regression from https://github.com/vitejs/vite/issues/130 but many things have change with Vite 2 (especially project structure) so it’s hard for me to identify why the bug reappeared (but with some guidance I’m happy to help).

Reproduction

Run projects and go to http://localhost:3000/just.a.test (or a similar URL).

System Info

  • vite version: 2.0.1 (with @vitejs/plugin-vue 1.1.4)
  • Operating System: Ubuntu 20.10
  • Node version: 15.10.0
  • Package manager (npm/yarn/pnpm) and version: Yarn 1.22.10

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 51
  • Comments: 22 (3 by maintainers)

Commits related to this issue

Most upvoted comments

Given that . are valid characters and URLs and that https://www.npmjs.com/package/vite-plugin-rewrite-all has now almost 30k downloads weekly (plus people using a raw plugin in their vite config). I think it would be great not to leave this hanging too much longer. The longer is stays open, the longer people keep finding workarounds and not use dots in their paths, despite being totally valid.

It’s worth noting that the plugin workaround breaks other features like opening files with vitest

There was an issue created about this already here: https://github.com/vitejs/vite/issues/2245 But it was closed because of the use of a workaround with a plugin, that may also help you while this is open. Maybe there is a limitation I’m not aware of, but this looks like a bug, and the other issue was wrongly closed.

Hey everyone, ran into the same issue today. If you want a workaround that doesn’t involve installing random “fix things” packages such as vite-plugin-rewrite-all, here is a simple middleware function you can add that works for me:

import fs from 'fs';

const dotPathFixPlugin = () => ({
  name: 'dot-path-fix-plugin',
  configureServer: (server) => {
    server.middlewares.use((req,_,next) => {
      const reqPath = req.url.split('?', 2)[0];
      if(!req.url.startsWith('/@') && !fs.existsSync(`.${reqPath}`)) {
          req.url = '/';
      }
      next();
    });
  }
});

Which just checks if the request is a path for an existing file (or special dev server path), and if not just rewrites it to /.

In your vite.config.js file you simply use it:

export default defineConfig({
  plugins: [
    // your other plugins
    dotPathFixPlugin()
  ],
  // rest of the stuff
}

And sure, there are “performance” implications to checking if a file exists, but as long as we are just talking a development server, I cannot see how this is a practical concern. I have to admit I never understood the modern obsession with ultra-fast / hmr, etc. when it means we have to sacrifice application design in favor of a good developer experience.

Edit

I should clarify I am working on a Solid.JS (SPA) application and only currently doing no SSR or SSG. Just in early development mode running a classic SPA.

@TLIQ In short, the workaround provided via the Vite plugin https://www.npmjs.com/package/vite-plugin-rewrite-all seems to work for people.

to follow up on @nicholasdgoodman’s solution (thanks!), depending on the rest of our setup, you may need to add a few extra checks, for example:

const dotPathFixPlugin = () => ({
  name: "dot-path-fix-plugin",
  configureServer: (server) => {
    server.middlewares.use((req, _, next) => {
      const reqPath = req.url.split("?", 2)[0];
      if (
        !req.url.startsWith("/@") && // virtual files provided by vite plugins
        !req.url.startsWith("/api/") && // api proxy, configured below
        !existsSync(`./public${reqPath}`) && // files served directly from public folder
        !existsSync(`.${reqPath}`) // actual files
      ) {
        req.url = "/";
      }
      next();
    });
  },
});

The behaviour seems to be because vite assumes that if the final segment of the path contains ., the user meant to navigate to a file. This is a pretty bad assumption to take because files without . are valid too (although, uncommon) and modern SPAs generally expect all paths except static assets to be routed to index.html so client-side routing can take effect.

One solution could be to check existance in the file system, and if the file is not there, serve index.html, similar to try_files is often configured in nginx to serve prod apps. With such an mechanism, no option like https://github.com/vitejs/vite/pull/2634 is necessary as it’ll work correctly in all cases, but it will involve one additional stat call on each request.

Hi, I have the same problem. In my case, it’s PocketBase adding a JWT token to the URL which I have no control over.

It’s not fixed in Vite 4 as it’s a breaking change. You can start using the Vite 5 beta to fix it. Vite 5 stable will come around mid October.