Vue-router redirect on page not found (404)
Asked Answered
T

7

120

I'm trying to redirect to 404.html on page not found using the router.beforeEach global hook, without much success (using Vueand Vue-Router 1.0):

router.beforeEach(function (transition) {
    if (transition.to.path === '/*') {
        window.location.href = '/404.html'
    } else {
        transition.next()
    }
});

This is not redirecting to page 404.html when a non-existing route is inserted, just giving me an empty fragment. Only when hard-coding some-site.com/* will it redirect to the expected some-site.com/404.html

I'm sure there's something very obvious here that I'm overlooking, but I cannot figure out what.


Please note that what I am looking for is a redirect to a new page, not a redirect to another route, which could be easily achieved by using router.redirect such as in these snippets:

router.redirect({
    '*': '404'
})

Whereas on my router.map, I could have the following:

 router.map({
    '/404': {
        name: '404'
        component: {
            template: '<p>Page Not Found</p>'
        }
    }
})
Thinner answered 22/10, 2016 at 15:7 Comment(3)
I don't have a solution yet, but what happens if you try to navigate to a route using <a> tag that is not handled by Vue router? Does it navigate out of the app?Gahnite
It will navigate to the expected route regardless of whether I use href or v-link (for instance, href="some-site.com/home" instead of v-link="{name:'home'}"). Not sure if that's what you're asking?Thinner
My apologies, I was thinking of how to get out of the app entirely even when we have <base href="/">. That was unrelated to your question anyways. I have posted my thoughts as an answer below, please try it out and let me know if it works!Gahnite
G
227

I think you should be able to use a default route handler and redirect from there to a page outside the app, as detailed below:

const ROUTER_INSTANCE = new VueRouter({
    mode: "history",
    routes: [
        { path: "/", component: HomeComponent },
        // ... other routes ...
        // and finally the default route, when none of the above matches:
        { path: "*", component: PageNotFound }
    ]
})

In the above PageNotFound component definition, you can specify the actual redirect, that will take you out of the app entirely:

Vue.component("page-not-found", {
    template: "",
    created: function() {
        // Redirect outside the app using plain old javascript
        window.location.href = "/my-new-404-page.html";
    }
}

You may do it either on created hook as shown above, or mounted hook also.

Please note:

  1. I have not verified the above. You need to build a production version of app, ensure that the above redirect happens. You cannot test this in vue-cli as it requires server side handling.

  2. Usually in single page apps, server sends out the same index.html along with app scripts for all route requests, especially if you have set <base href="/">. This will fail for your /404-page.html unless your server treats it as a special case and serves the static page.

Let me know if it works!

Update for Vue 3 onward:

You'll need to replace the '*' path property with '/:pathMatch(.*)*' if you're using Vue 3 as the old catch-all path of '*' is no longer supported. The route would then look something like this:

{ path: '/:pathMatch(.*)*', component: PathNotFound },

See the docs for more info on this update.

Gahnite answered 22/10, 2016 at 16:0 Comment(3)
I think the real answer was server side handling, as detailed in the "Please Note" section above. Your router.beforeEach does the same thing as in my method. Your server just needs to handle 404.html as a special case, which you can test only in an actual server, not vue-cli.Gahnite
Your solution worked just fine, thank you so much! Also, thank you for the hint concerning server-side handling with router.beforeEach. I'm using a local machine (vagrant+virtualbox) running nginx. I'll play around with the configuration and see if I can come up with a server-side solution.Thinner
As Vue Router 2.2.0, I'm using path: '*' in the router definition to "catch all" non-defined routes, as stated in the docsBreeches
R
50

@mani's response is now slightly outdated as using catch-all '*' routes is no longer supported when using Vue 3 onward. If this is no longer working for you, try replacing the old catch-all path with

{ path: '/:pathMatch(.*)*', component: PathNotFound },

Essentially, you should be able to replace the '*' path with '/:pathMatch(.*)*' and be good to go!

Reason: Vue Router doesn't use path-to-regexp anymore, instead it implements its own parsing system that allows route ranking and enables dynamic routing. Since we usually add one single catch-all route per project, there is no big benefit in supporting a special syntax for *.

(from https://next.router.vuejs.org/guide/migration/#removed-star-or-catch-all-routes)

Rowboat answered 3/10, 2020 at 15:47 Comment(1)
What about just replacing it with (.*)* as :pathMatch would just be the param that the match is assigned to, correct? So it could be { path: '/:thisIsMyCustomErrorCatchAll(.*)*', component: PathNotFound },. I looked at the docs to confirm, and couldnt find anything to go against this. next.router.vuejs.org/guide/essentials/…Parkerparkhurst
G
5

This answer may come a bit late but I have found an acceptable solution. My approach is a bit similar to @Mani one but I think mine is a bit more easy to understand.

Putting it into global hook and into the component itself are not ideal, global hook checks every request so you will need to write a lot of conditions to check if it should be 404 and window.location.href in the component creation is too late as the request has gone into the component already and then you take it out.

What I did is to have a dedicated url for 404 pages and have a path * that for everything that not found.

{ path: '/:locale/404', name: 'notFound', component: () => import('pages/Error404.vue') },
{ path: '/:locale/*', 
  beforeEnter (to) {
    window.location = `/${to.params.locale}/404`
  }
}

You can ignore the :locale as my site is using i18n so that I can make sure the 404 page is using the right language.

On the side note, I want to make sure my 404 page is returning httpheader 404 status code instead of 200 when page is not found. The solution above would just send you to a 404 page but you are still getting 200 status code. To do this, I have my nginx rule to return 404 status code on location /:locale/404

server {
    listen                      80;
    server_name                 localhost;

    error_page  404 /index.html;
    location ~ ^/.*/404$ {
      root   /usr/share/nginx/html;
      internal;
    }

    location / {
      root   /usr/share/nginx/html;
      index  index.html index.htm;
      try_files $uri $uri/ @rewrites;
    }

    location @rewrites {
      rewrite ^(.+)$ /index.html last;
    }

    location = /50x.html {
      root   /usr/share/nginx/html;
    }
}
Guam answered 30/6, 2020 at 6:29 Comment(1)
The issue with this leads to the 200 status code for 404 page, Which is not acceptable according to the SEO perspective. Can anyone suggest how to set the 404 status code in VueJS for 404 page?Weatherglass
S
5

path: "*" is outdated. It belongs to vue-router2 on vue-router3. You can catch all 404 error pages by using

/:pathMatch(.*)*

It works well.

Selectivity answered 10/9, 2022 at 16:41 Comment(0)
C
4

Do not use redirect; use beforeEnter

const router = [

    // { path: '*', redirect: '/404'},
    { path: '*', beforeEnter: (to, from, next) => { next('/404') } },
    {
        path: '/404',
        name: '404',
        component: () => import('@/views/404')
    }
]
Chalcography answered 22/7, 2021 at 9:42 Comment(1)
Could you expand a bit why is it better that way?Thinner
S
0

This will make it work too:

{
    path: '/*/',
    name: 'PageNotFound',
    component: () => import('../views/PageNotFound.vue')
}
Supererogation answered 5/8, 2022 at 9:29 Comment(1)
This will not have the desired effect op asked forAkihito
M
0

I do not specifically answer your question, but I have an answer for anyone who is looking for page not found route syntax in Vue js just like I was looking for and came here.

Here are two things you can do:

  1. First, create a component in the views and write some HTML like error 404!

  2. Then import that component in index.js in router folder and import it there like:

  3. import yourcomponentName from '../views/yourcomponentName.vue'

  4. const routes = [ { path: '/:catchAll(.*)', ->This is the regex pattern name: 'whatever name you want to give', component: yourComponentName }]

  5. If it does not work then use this

  6. const routes = [ { path: '/:pathMatch(.*)', ->This is the regex pattern name: 'whatever name you want to give', component: yourComponentName }]

    Remove the comment: 'This is the regex pattern it is writtern for understanding'

Mcclintock answered 24/8, 2022 at 15:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.