Vue Router return 404 when revisit to the url

Publish date: 2024-05-22

I just enable Vue router history mode. And it work fine when I visit to vue routing via v-href or href. But, when I try to refresh that page or go directly from browser address bar, it just return 404. Is there any option to accept refresh/revisit to that url?

The following is my Vue router configuration

var router = new VueRouter({ hashbang: false, history: true, mode: 'html5', linkActiveClass: "active", root: '/user' }); 

21 Answers

I think you are missing that SPA is not server side rendering. At least for the majority. So, when you access /anything your web server won't redirect it to index.html. However, if you click on any vuejs router link, it will work due to the fact that the javascript got in action, but not the server side.

To solve this, use .htaccess and redirect all requests to index.html like so

<ifModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteRule ^index\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.html [L] </ifModule> 

Hope it helps someone!

3

By refreshing the page you are making a request to the server with the current url and the server returns 404. You have to handle this on your web framework or web server level.

This article contains example server configuration: https://v3.router.vuejs.org/guide/essentials/history-mode.html

4

That is too simple, you just have to add this config on your server:

Apache

<IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteRule ^index\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.html [L] </IfModule> 

Then if you are on Nginx:

location / { try_files $uri $uri/ /index.html; } 

And that's all

For more information, visit this Link

2

Even better (on Ubuntu) if you have root access would be to modify /etc/apache2/apache2.conf, since even Apache themselves discourage the use of .htaccess under such circumstances.

Note that first, you have to do

sudo a2enmod rewrite 

Then, add the following block to /etc/apache2/apache2.conf:

<Directory /var/www/html/> RewriteEngine On RewriteBase / RewriteRule ^index\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.html [L] </Directory> 

After this, do

sudo systemctl restart apache2 

For me, this works flawlessly, as it not just redirects routes created by Vue back to /, but actually refreshes the website to the same route, so you're still on the same view after refreshing.

2

If you don't really care about the hash in the url, you can just set the mode to hash in you router configuration file: mode: 'hash' That works fine without the need to set up the server side.

const router = new VueRouter({ routes: Routes, mode: 'hash' }); 

Hash mode comes by default in vue, so it should work even if you leave blank instead of mode: 'hash'.

1

If someone is dealing with this issue in .NET Core as the backend of the app, a nice approach is a simple fallback handler in the Startup.cs of our .NET Core application:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { ....Your configuration app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); //handle client side routes app.Run(async (context) => { context.Response.ContentType = "text/html"; await context.Response.SendFileAsync(Path.Combine(env.WebRootPath, "index.html")); }); } } 

For more details: http://blog.manuelmejiajr.com/2017/10/letting-net-core-handle-client-routes.html

0

For Netlify, add the following to a public/_redirects file...

/* /index.html 200 

See https://www.netlify.com/docs/redirects/#history-pushstate-and-single-page-apps

3

Just put the code in the .htaccess file in the same folder and that's it. No need to reboot or Apache restart.

RewriteEngine On RewriteBase / RewriteRule ^index\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.html [L] 
1

if any one of is facing the issue even after trying above solution, please find below method. If you have vue.config.js which has

module.exports = { publicPath: process.env.PUBLIC_URL || "", // <-- this is wrong ... }; 

either remove the vue.config.js or try removing the publicPath from the exports object.

Also you can try below method if you dont want to remove the publicPath

module.exports = { publicPath: process.env.PUBLIC_URL || "/", default) transpileDependencies: ["vuetify"], }; 

In my case it was happening in a SPA hosted in azure blob storage as static website under an azure CDN. The solution of the following question solved the issue for me (fix applied in CDN endpoint): Hosting SPA with Static Website option on Azure Blob Storage (clean URLs and Deep links)

In my case, I had handled it through virtual host:

Apache

goto /etc/apache2/sites-available and clone 000-default.conf with you domain name i.e. app.abc.com.conf and open it by sudo nano app.abc.com.conf and then place below snippet:

Vue Doc Reference: Click here
StackOverflow Reference: Click here
<VirtualHost *:80> ServerName app.abc.com DocumentRoot /var/www/html/project_name/dist/ ErrorLog ${APACHE_LOG_DIR}/app.abc.com.error.log CustomLog ${APACHE_LOG_DIR}/app.abc.com.access.log combined <Directory /var/www/html/project_name/dist> FallbackResource /index.html </Directory> </VirtualHost> 
Niginx Vue Cli (SPA) virtual host:
Vue Doc Reference: Click here
server { listen 80; root /var/www/html/project_name/dist; server_name app.abc.com; index index.html index.htm index.php; location / { try_files $uri $uri/ /index.html; } } 

Note: Tested on ubuntu server

For someone seeing this using an Express backend, there is the middleware connect-history-api-fallback that is implemented like this

const express = require('express'); const history = require('connect-history-api-fallback'); const app = express(); app.use(history({ index: '/' //whatever your home/index path is default is /index.html })); 

Or with Native Node

const http = require('http') const fs = require('fs') const httpPort = 80 http.createServer((req, res) => { fs.readFile('index.htm', 'utf-8', (err, content) => { if (err) { console.log('We cannot open "index.htm" file.') } res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }) res.end(content) }) }).listen(httpPort, () => { console.log('Server listening on: http://localhost:%s', httpPort) }) 

Both are suggestions in the documentation.

In my case, I used Vue's own configuration to tackle this issue. The idea here is to utilize pages option. So in the file vue.config.js, we will add the following sample of code:

module.exports = { pages: { // This is our main page which refers to the main Javascript file index: "src/main.js", // Next, we list all other pages in the application, // and let all of them refer to the main Javascript file as well. other_page: "src/main.js", }, }; 

With this implementation, if we stay at other_page and reload, the application would still work without any 404 error displayed.

Anyone else facing the same problem, I just realized that

"book/:bookId/read" // this will not work after page reload 

and this are different

"/book/:bookId/read" // this is what works even after page reload 

This is of course after following what other fellas have suggested up there more importantly the catch all route in your app server side. I actually don't know why this worked, but any one with any ideas can let us know.

I'm using Razor Pages with .net core 2.2 and I got this working:

app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller}/{action=Index}/{id?}"); routes.MapRoute( name: "spa-fallback", template: "{*url:regex(^((?!.css|.map|.js|sockjs).)*$)}", defaults: new { controller = "Index", action = "Index" }); }); 

Coming in late here, but.....

If anyone's interested in a workaround for this, I simply did this:

  • Redirect requests to known client-side URLs to the index (but only known client-side URLs, that way, you can keep 404s for actual page-not-found URLs). How you do this is dependent upon your server. I was using flask at the time, and just added more routes.

  • Add an attribute to the root vue element

  •  <div id="content" redirect_to="{{redirect_to}}"></div>

    Where my view function was replacing the parameter with the actual route requested by the URL

  • In the root vue component, check for this attribute and retrieve its value, as so:
  • if (this.$el.attributes['redirect_to'].value !== "") path = this.$el.attributes['redirect_to'].value

  • Make a call to $router.push with the value of the attribute
  • this.$router.push(path).catch(err => {})

    (The catch is needed otherwise the router will throw a NavigationDuplicated error.)

    I did this because the entire point of urls in an SPA is because they point to specific resources and I wanted my users to be able to use the URLs to get back to those same resources.

    Hopefully this either helps someone or points them in the right direction to make it work for their specific needs.

    I am also facing the same problem. After adding this code in routes -> web.php the issue is resolved.

     Route::get('/{vue_capture?}', function () { return view('admin.dashboard'); })->where('vue_capture', '[\/\w\.-]*'); 

    Redirect this route to your parent blade view.

    Just as explained by @SERPRO, Vuejs, Angular, Reactjs, etc are all build on the concept of Single Page Application (SPA), so what I did on my Apache server was simply tell the server to load index.html for any path value after the origin. below are the changes that worked for me. on my virtual host file, I added a simple rewrite rule to always load index.html and not to redirect.

    RewriteEngine On RewriteRule "^/auth/account/confirm" "/index.html" 

    And so if a user enters http://my-origin/account/confirm, the server will load the index.html with the app.vue file bound to it, and the app.vue file router will now pick the path account/confirm and determine the appropriate component to render

    Then run

    apachectl restart service httpd restart 

    In my case the 404 error not only happend on a page reload but also when using the navigation buttons of the browser.
    The problem were special characters like ä, ö, etc. in the path. First, the routing worked very well with those but when using the browsers navigation buttons or on a page reload the page can't be found anymore. After I replaced special characters with normal letters the routing worked again.
    Maybe this helps somebody else.

    My case is Apache server. There are 2 ways to configure:

    I) Put a .htaccess file in the root vue source, (usually it is /var/www/html/)

    The code and explanation for .htaccess are well explained by @Tiago Matos already.

    The only one thing he didn't mention is to let Apache know a user can configure the Apache in .htaccess file under the root vue source (here is /var/www/html/), to do this:

  • Open httpd config file to edit:

    sudo vi /etc/httpd/conf/httpd.conf

  • Change AllowOverride to All

    <Directory /var/www/html> AllowOverride All

  • Restart Apache to apply a new configuration

    sudo systemctl restart httpd

  • II) Edit directly configuration of Apache: /etc/apache2/apache2.conf as @Tobias Feil mentioned. (Although not tested yet)

    Change your router history from createWebHistory to createWebHashHistory

    1

    ncG1vNJzZmirpJawrLvVnqmfpJ%2Bse6S7zGiorp2jqbawutJoam9raW6AcoWOr6yeZaKkwrWx0Wapnqylp7tugI9tZLCglaN6s7HVoqqirF2pvG7Ax55krqqc