How to handle 404 error request in vuejs SPA with nginx server How to handle 404 error request in vuejs SPA with nginx server vue.js vue.js

How to handle 404 error request in vuejs SPA with nginx server


I think it is similar to Node solution - you should repeat all your routes in nginx config to return 404 status code correctly, the main idea is that you should use "equals" modifier in locations and define error_page to return same index.html file but with 404 status code, example:

server {    listen       80;    server_name  localhost;    root /my/dir/with/app    error_page  404 /index.html;    location = / {        try_files $uri $uri/ /index.html;    }    location = /books {        try_files $uri $uri/ /index.html;    }    # example nested page    location = /books/authors {        try_files $uri $uri/ /index.html;    }    # example dynamic route to access book by id    location ~ /books/\d+$ {        try_files $uri $uri/ /index.html;    }}

Probably this config can be simplified or improved because I am not very good at nginx configuration but it works.


I solved this the easy way by breaking out of the Vue ecosystem else it wont work or would take a lot of effort.

Make the following route in your Vue router:

{  path: '*',  name: 'PageNotFound',  component: PageNotFound}

The PageNotFound component should have the following code:

<script>export default {  name: 'PageNotFound',  created() {    window.location.href = "/404/"  }}</script>

The nginx config should return a 404 on getting /404/ route:

server {    ...    location ~ ^/404/$ {        return 404;    }    ...}

I don't think it would work in a server side rendering environment. In such a case you might need to put the statement containing window.location.href in the mounted method.


For whoever lands into this issue and to save you hours of headaches.

Caveats with above answers

  1. Simply reloading the page with a new URL (e.g. /notfound) does not solve it since that means that a potential spider already received 200.
  2. Simply copying the routes, is a half-solution. That works with URLs that are never going to change and by checking the validity of the URL structure. So for example it can check if the book id in books/123 has the right format, but it can't check if books/123 actually exists in the backend.

Here's two approaches that can tackle the above issues

  1. Have Nginx to make a mirrored subrequest to the backend to check if the resource actually exists. Then always return index.html but with the status from the subrequest's response. This is very tricky with Nginx, since by design it makes it very hard to combine answers.
  2. Have the backend API to return index.html for Accept: text/html. Then Nginx simply needs to forward responses.

The first solution is a pain in the ass for someone not familiar with Nginx. It requires to get Lua with OpenResty and then again you will run into all kinds of quirks with how Nginx works. You end up with a lot of hard-to-read code and that makes it extra hard if you further-more want to introduce caching.

The second solution is easier. The only possible negative is that it means that you can't view the API from the browser if that's something currently you have in place.

nginx.config (when API responds with index.html on Accept: text/html)

    location / {      try_files $uri $uri/ @fallback;    }    location @fallback {      rewrite ^(.*) /api$1 break;      proxy_set_header "Accept" "text/html";      proxy_pass http://localhost:8000;    }

In this case, Nginx will try first to serve the file and in case it doesn't find it locally, it will go through the fallback.

In the fallback we rewrite the URI to match what the backend server expects. In this example I prepend api/ to each request. Then I add the header Accept: text/html so that the backend API will respond with the index.html instead of JSON. And lastly we directly give back the response to the client.

This has the below benefits:

  1. It does not rely on Nginx so it can work with any reverse proxy. Most importantly it does not rely on the proxy server to have certain features.
  2. Works even during development without having Nginx running.
  3. Easy to write tests for. You simply have to test your backend API for spitting out index.html when given Accept: text/html for any endpoint.
  4. Does not require you to manually update Nginx configuration with each new endpoint.

Furthermore you can change the config to make Nginx follow redirects internally, and possibly not even having to look at the backend API for URLs that never change.