Vue Router return 404 when revisit to the url
Asked Answered
V

23

70

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'
});
Veratridine answered 4/4, 2016 at 9:47 Comment(0)
B
66

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

Blakeley answered 4/4, 2016 at 10:4 Comment(4)
in my case, using a sub dir instead of root dir in IIS, ex: localhost/myvueapp, then the rewrite url should be: <action type="Rewrite" url="/myvueapp/" />Seabolt
Ironically your link returns 404 page :)Purl
How would you make this works in "npm run dev" ? Any history mode returns: >> Not Found The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.Irreverent
it's worked on meGentleness
S
82

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!

Soinski answered 20/8, 2017 at 2:47 Comment(3)
If you're using an nginx host there's a helpful gist here.Ridglea
Ironically, the gist link returned 404 for me... :(Lexicologist
Thank you. Works great on apache server.Ory
B
66

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

Blakeley answered 4/4, 2016 at 10:4 Comment(4)
in my case, using a sub dir instead of root dir in IIS, ex: localhost/myvueapp, then the rewrite url should be: <action type="Rewrite" url="/myvueapp/" />Seabolt
Ironically your link returns 404 page :)Purl
How would you make this works in "npm run dev" ? Any history mode returns: >> Not Found The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.Irreverent
it's worked on meGentleness
U
23

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

Uchish answered 6/12, 2020 at 0:40 Comment(2)
What if I am serving my vue app with aws cloudfrontHymnology
the additional try_files config for Nginx does not work for me. Any requests to localhost/subPage leads to a 404.Diminished
P
11

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'.

Pliny answered 27/5, 2019 at 9:12 Comment(1)
could you provide an example?Contretemps
Z
10

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.

Zoltai answered 4/2, 2020 at 15:13 Comment(2)
I tested Nginx and Apache and I refresh the page and they works both for me, Thank youjAtherosclerosis
This works bestNarial
W
5

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

Wolof answered 30/10, 2017 at 19:16 Comment(0)
A
4

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

Alexina answered 4/4, 2016 at 9:48 Comment(3)
Can I ask you how do you handle a 404 when doing this? I am in a similar situation and this works, but then not sure how to handle 404 errors.Frazzle
@Frazzle try the documentation ~ router.vuejs.org/guide/essentials/history-mode.html#caveatAlexina
Wish I found that link sooner!Frazzle
W
4

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]
Wiltonwiltsey answered 26/11, 2021 at 12:43 Comment(1)
Hi, your code worked on cPanel. Could you please explain it line by line? Thank you! I guess it redirects all URLs to index? (or file request only)?Dennis
C
3

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"],
};
Crockett answered 1/8, 2020 at 15:36 Comment(0)
K
2

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)

Karlik answered 20/11, 2020 at 9:55 Comment(0)
K
2

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

Keli answered 16/2, 2022 at 15:22 Comment(0)
D
1

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.

Duplication answered 4/7, 2018 at 4:5 Comment(0)
G
1

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.

Gramnegative answered 22/11, 2021 at 15:17 Comment(0)
R
0

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.

Ressieressler answered 16/2, 2019 at 8:53 Comment(0)
P
0

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" });

        });
Policlinic answered 26/7, 2019 at 17:55 Comment(0)
A
0

Coming in late here, but.....

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

  1. 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.

  2. 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

  1. 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

  1. 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.

Alphabetical answered 21/1, 2020 at 7:12 Comment(0)
O
0

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.

Organo answered 29/7, 2021 at 8:1 Comment(1)
Thanks for sharing but it is good that you specify that this is a laravel approachMarte
C
0

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
Canvas answered 27/9, 2021 at 9:50 Comment(0)
R
0

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.

Rhythmandblues answered 18/10, 2021 at 22:23 Comment(0)
C
0

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:

  1. Open httpd config file to edit:

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

  2. Change AllowOverride to All

    <Directory /var/www/html> AllowOverride All

  3. 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)

Cleanthes answered 5/1, 2023 at 4:56 Comment(0)
N
0

Change your router history from createWebHistory to createWebHashHistory

Neary answered 26/8, 2023 at 0:35 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Cuthburt
D
0

With that explained for production, let's move on to deployment adaptation for local development: this can be achieved by configuring a local proxy.

In vue.config.js, for example: suppose request address to https://localhost:4210/workbench

// vue.config.js
module.exports = {
  publicPath: '/',
  pages: {
    'index': 'src/main.ts',
  },
  devServer: {
    port: 4210,
    https: true,
    proxy: {
      "/workbench": {
        target: "https://localhost:4210",
        bypass: function (req, res, proxyOptions) {
          return '/index.html';
        },
      }
    }
  },
  // ...
};
Devaluate answered 27/3 at 3:53 Comment(0)
M
0

it may help someone so i decided to comment.. The vue3 docs clearly states here: https://router.vuejs.org/guide/essentials/history-mode#HTML5-Mode, that for SPA's it will need an extra config on the server..

it varies based on the web/app server like so, Apache, nginx or IIS .

for IIS i had to do this setup in the web.config:

<?xml version="1.0" encoding="UTF-8"?>
    <configuration>
       <system.webServer>
         <rewrite>
            <rules>
              <rule name="SPA Routes" stopProcessing="true">
                 <match url=".*" />
                   <conditions logicalGrouping="MatchAll">
                      <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                      <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                   </conditions>
                   <action type="Rewrite" url="/index.html" />
               </rule>
           </rules>
       </rewrite>
   </system.webServer>
</configuration>
Metalinguistic answered 2/7 at 9:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.