Vue - How to create 404 error page with 404 response code - vue.js

How can I create 404 error page with 404 response code in Vue? Here is the route for 404.
{
path: "*",
name: "404",
component: load("404"),
alias: "/404"
}

You won't be able to set the HTTP status code in a Single-Page Application - all the routing is done on the same page in the browser so once the page is loaded no more HTTP status codes will be received.
However, if you try to load a non-existent route by directly typing/copying the URL into the address bar of the browser - then you can use nginX (or whatever server software you are using) to signal 404 HTTP status:
server {
error_page 404 /404.html;
location / {
try_files $uri $uri/ =404;
}
}
But this is not a practical/wise approach - basically, you want every non-existent path to be resolved to /index.html so that your SPA is always loaded and once loaded - it will detect that this route does not exist and will render your 404 component.
So your nginX config should look like this:
server {
...
location / {
try_files $uri /index.html$is_args$args;
}
}
The $is_args variable will add ? if $args is not empty while $args will provide the query parameters (if any).

Your route definition looks ok, except that I don't know your load function does, i.e. how it resolves to a Vue component. But in general your code follows the common approach as described in the Vue router docs. Usually you won't need a name or an alias here since this route is not used explicitly. Just put in a component that shows your "not found" content and you should be good to go:
{
path: "*",
component: PageNotFound
}
Since this is very close to the code you provided, please explain what exactly gives you a problem.

Like IVO GELOV wrote, you have to force this on the server. I came across the issue with Apache (IVO GELOV provides help on nginx).
In the vue router (Vue 3):
const routes = [
{
// Your other routes here
// ...
{
path: '/404',
name: '404',
component: Error404,
},
{
path: '/:pathMatch(.*)*',
beforeEnter() { window.location.href = "/404" },
},
]
With the last route item, all non-matched routes will be redirected to /404. I use beforeEnter to avoid creating a component and window.location.href to get out of the Vue application.
In Apache .htaccess:
<IfModule mod_alias.c>
Redirect 404 /404
</IfModule>
This will add a 404 error code to the /404 url.
Then the /404 route from the vue router config will call the Error404 component that you have to define and import like any other Vue component!
This answer is heavily inspired by:
https://stackoverflow.com/a/62651493/14217548

Related

Catch-all route for production build of Vue Router/Vite-based SPA

I have a Vue3-based SPA using Vue Router and Vite as dev server. The app has three valid URL paths:
/
/first
/second
For production, it will be deployed on Apache under a prefix, i.e. the production URL paths will be:
/prefix
/prefix/first
/prefix/second
What I want to achieve is that clients should be redirected to a valid URL path (e.g. /prefix) of the application even when they initially request an invalid URL path, such as /prefix/invalid.
For this purpose, I've defined the following router.js:
import { createWebHistory, createRouter } from "vue-router";
const routes = [
{
path: "/first",
component: () => import("./components/First.vue")
},
{
path: "/second",
component: () => import("./components/Second.vue")
},
{
path: "/:anything+",
redirect: "/"
},
];
const router = createRouter({history: createWebHistory(import.meta.env.BASE_URL), routes});
export default router;
The third route entry defines a catch-all route that will match whenever clients request an invalid URL path. This works fine in development on Vite, i.e. when serving the app via npm run dev -- --base='/prefix/'. Even when the browser initially requests /prefix/invalid, the app's /prefix route is loaded and displayed.
When deploying with npm run build -- --base='/prefix/' for production, however, the catch-all route starts working only after clients have initially requested /prefix or /prefix/index.html and the SPA has been loaded. When they initially request /prefix/invalid or even /prefix/first or /prefix/second, Apache responds with 404 Not Found. This is of course because the whole routing is implemented client-side in JavaScript, so my catch-all route and everything else routing-related will only work once the SPA itself has been loaded and its JavaScript is executing in the browser.
My question is: is there a way to make initial requests for invalid URL paths work in production on Apache like in dev on Vite? I tried adding a file public/.htaccess like this:
ErrorDocument 404 import.meta.env.BASE_URL
But the expression import.meta.env.BASE_URL, which Vite statically replaces with the base config option value in router.js, is not replaced in this file - which is consistent with the documentation of the public directory. Hence this approach doesn't work.
Not sure whether I'm following the right path with .htaccess or whether my business problem has any better -possibly simpler- solution?
The problem can be solved by generating .htaccess dynamically using a simple Vite plugin, which I define inline in vite.config.js for simplicity.
// vite.config.js
import { defineConfig } from 'vite'
import vue from '#vitejs/plugin-vue'
// A private Vite plugin for generating .htaccess dynamically
function generateHtaccess () {
// Resolved Vite configuration, including "base" option
let viteConfig;
return {
name: 'generate-htaccess',
// Rollup Plugin API output generation hook
// See https://rollupjs.org/guide/en/#generatebundle
generateBundle() {
this.emitFile({
type: 'asset',
fileName: '.htaccess',
source: `ErrorDocument 404 ${viteConfig.base}\n`
});
},
// Vite Plugin API specific hook
// See https://vitejs.dev/guide/api-plugin.html#configresolved
configResolved(resolvedConfig) {
viteConfig = resolvedConfig;
},
};
}
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), generateHtaccess()],
});
A non-prefixed production build, such as npx vite build, will hence generate dist/.htaccess with the following content:
ErrorDocument 404 /
while specifying a prefix, e.g. npx vite build --base='/~user/' will generate:
ErrorDocument 404 /~user/
With this .htaccess, clients initially requesting any invalid URL path in production on Apache will be properly redirected to the same catch-all route -the app's root directory- as defined in router.js. This behavior will work consistently for any --base value supplied at build time.

vuejs add route after loading a content file

I developed a CMS and vuejs App to configurate custom terminal in shops. after loading the initial CMS File from the server I want to add the "/" and "*" path to the routes. how can I do this. for some reasons I would like to have the router initialized before I load the content.
I thought I could add a route with:
Vue.router.routes.push({
path: "*",
redirect: this.getStartPageType() // gets the homepage from the store
})
I get always "Cannot read property 'push' of undefined".
How could I do this. is it possible to a a method to the routes.
when adding this to the routes,
{
path: "*",
redirect: () => {
store.getters.getAppStartPageType;
}
}
I got an error: fullpath not definded in
<router-view :key="$route.fullPath" v-if="loaded==true"/>
and when entering a wrong path into the bwoser url string then I got "invalid redirect option: undefined"

vue-router does not redirect

Despite of multiple posts on the subject I can't find what is going wrong. I been followed this example.
Here is my VueRouter instance.
const router = new VueRouter({
mode: "history",
routes: [
{
path: "/",
component: require("./components/producer-table.vue").default
},
{
path: "/producer/:producerId",
component: require("./components/variable-table.vue").default,
props: true
},
{ path: "*", redirect: "/" }
]
});
If I route to a wrong url programmatically (this.$router.push({path: "/wrong});) I am redirected to http://localhost/.
Everything seems to work fine except that when I set a wrong address in Chrome url bar I get the following message:
Cannot GET /wrong
I would expect http://localhost/wrong to be redirected to http://localhost/. I'm quite new using vue router and I'm must miss something. Any ideas?
This is because your webserver doesn't know GET /wrong should be redirected to the index.html file; from there on, vue-router can take over.
Some examples on how to redirect different web servers;
Apache: htaccess rewrite all to index.html
nginx: Rewrite all requests to index.php with nginx
webpack-dev-server:
/// in your webpack.config
devServer: {
historyApiFallback: {
index: '/'
}
}

Nuxt 404 error page should redirect to homepage

I am looking for a way to always redirect to homepage when a page doesn't exist using Nuxt.Js.
Our sitemap generation had some problems a few days back and we submitted wrong urls that do not exist. Google Search Console shows a big number of 404 and we want to fix them with 301 redirect to homepage.
I tried this
created() {
this.$router.push(
this.localePath({
name: 'index',
query: {
e: 'er'
}
})
)
}
and although the page redirects to homepage successfully I think Google will have problems with this since the pages initially renders with 404.
I also tried this
async asyncData({ redirect }) {
return redirect(301, '/el?e=rnf')
},
but didn't work (same with fetch)
Any ideas on a solution to this?
Never redirect to home if page is not found as you can see in this Google's article: Create custom 404 pages
instead, redirect to 404 error page
Just use error
async asyncData({ params, $content, error }) {
try {
const post = await $content('blog', params.slug).fetch()
return { post }
} catch (e) {
error({ statusCode: 404, message: 'Post not found' })
}
}
do not forget to creat an error page in layout folder error.vue
You are able to create a default 404-page in nuxt - just put a file with a name _.vue in your ~/pages/ dir. This is your 404-page :)
or you can use another method to create such page: https://github.com/nuxt/nuxt.js/issues/1614 but I have not tried it
Then add a simple 404-redirect-middleware to this page:
// !!! not tested this code !!!
middleware: [
function({ redirect }) {
return redirect(301, '/el?e=rnf')
},
],
Personally I would advise to create a 404 page which provides a better user experience in comparison to being redirected to a homepage and potentially being confused about what happened.
In order to create a custom error page, just create error.vue file in the layouts folder and treat it as a page. See the official documentation. We've implemented this plenty of times and Google has never complained about it.
Still, gleam's solution is clever and if it serves the purpose, very well. Just wanted to point out another solution.
If you need to provide custom routes to your users like domain.com/<userID>
then putting a file with a name _.vue in your ~/pages/ directory will not work, because you'll need it for your custom user routes.
For maximum flexibility use the layouts folder as mentioned by Dan
Create a file called _.vue at pages directory with content:
<script>
export default {
asyncData ({ redirect }) {
return redirect('/')
}
}
</script>

Laravel mix vuejs router history mode

I am using laravel mix and vuejs for my app and everything works fine.
Now I tried to change the VueRouter to history mode:
const router = new VueRouter({
mode: 'history'
})
On the ngix server, I added the catch all rule:
location / {
try_files $uri $uri/ /index.html;
}
Because of that its still working, when accessing or refreshing top level urls like "/pages" or "/contact".
The only problem is with sublevel urls like /pages/1 or /foo/bar/boo. Clicking on a router link still works, but if I try to refresh the page on /pages/1 or if I try to directly access it (enter /pages/1 in browser) its not working, since the browser tries to access the assets from /pages/js/2.js instead of /js/2.js
Ok, I just found it out - I had to set the public path to an absolute path in my webpack.mix.js file:
mix.webpackConfig({
...
output: {
...
publicPath: "/"
}
});