Blazor WASM 404 error and fix for GitHub Pages

The project structure of a Blazor WASM project is in some places very different than other frontend frameworks in the .NET stack. One of the nice things about Blazor WASM is that it can be hosted by any static file host. A problem with this is that routing is handled in many different ways on different hosts. In this article, we will look at the 404 error that often occurs when accessing a route directly on a GitHub Pages hosted Blazor WASM page and how to fix it.

Blazor WASM 404 error and fix for GitHub Pages

The problem

Previously we have often used the IIS to host our websites and managed routing and error handling through the web.config file. We can't do this when using static file hosts. GitHub Pages is not configured to support SPA (Single Page Application) routing out of the box, but we can make some changes to our project to make it work.

First, we need to see when the problem occurs. If we have some Blazor WASM page that is hosted on GitHub Pages that uses routing meaning that it has more than one page. Then we might have an URL like the following:

https://myaccount.github.io/MyProject/MyPage/

We can nicely go to this page using the navigation in the website or when we run the project locally. But if we try to access this URL directly we will be faced with the following page.

GitHub 404 page
404

File not found

The site configured at this address does not contain the requested file.

If this is your site, make sure that the filename case matches the URL.
For root URLs (like http://example.com/) you must provide an index.html file.

The reason

The problem occurs because of how GitHub Pages serves files. If it sees something that looks like a directory in the URL after the expected https://myaccount.github.io/MyProject/ then it will assume that you have some folder e.g. /MyPage that has an index.html file available to serve. We don't actually have a directory for /MyPage as it is just a route that we have defined in Blazor to make the experience of using the website better for the user.

The solution

The solution consists of two parts. First, we will need to redirect the user to some known page where we can control their further navigation. And then we will need to tell the Blazor WASM website where the user actually wanted to go.

We catch the user by creating a file called 404.html in the wwwroot folder of the project which is what gets published when using GitHub Pages to host. GitHub Pages automatically redirects users to this page instead of the screen we saw earlier. It shows this page but maintains the same URL which we can then extract and use to recover the state. The content of the 404.html is the following:

  
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Single Page Apps for GitHub Pages</title>
    <script type="text/javascript">
      // Single Page Apps for GitHub Pages
      // MIT License
      // https://github.com/rafgraph/spa-github-pages
      // This script takes the current url and converts the path and query
      // string into just a query string, and then redirects the browser
      // to the new url with only a query string and hash fragment

      // If you're creating a Project Pages site and NOT using a custom domain,
      // then set pathSegmentsToKeep to 1 (enterprise users may need to set it to > 1).
      // This way the code will only replace the route part of the path, and not
      // the real directory in which the app resides, for example:
      // https://username.github.io/repo-name/one/two?a=b&c=d#qwe becomes
      // https://username.github.io/repo-name/?/one/two&a=b~and~c=d#qwe
      // Otherwise, leave pathSegmentsToKeep as 0.
      var pathSegmentsToKeep = 1;

      var l = window.location;
      l.replace(
        l.protocol + '//' + l.hostname + (l.port ? ':' + l.port : '') +
        l.pathname.split('/').slice(0, 1 + pathSegmentsToKeep).join('/') + '/?/' +
        l.pathname.slice(1).split('/').slice(pathSegmentsToKeep).join('/').replace(/&/g, '~and~') +
        (l.search ? '&' + l.search.slice(1).replace(/&/g, '~and~') : '') +
        l.hash
      );

    </script>
  </head>
  <body>
  </body>
</html>

This comes from the GitHub repository https://github.com/rafgraph/spa-github-pages which is a repository that contains some general useful files for using SPA frameworks on GitHub Pages.

Now we have been redirected to the index.html file and all paths, query strings, and hashtags (anchor links) have been passed on. The query strings are combined in a single query string called q and the paths are placed in a query string called p.
We then need to decode these query strings and navigate to the correct page. This is done with the following code snippet that we will need to place in the head of our index.html file (the earlier the better).

<script type="text/javascript">
// Single Page Apps for GitHub Pages
// MIT License
// https://github.com/rafgraph/spa-github-pages
// This script checks to see if a redirect is present in the query string,
// converts it back into the correct url and adds it to the
// browser's history using window.history.replaceState(...),
// which won't cause the browser to attempt to load the new url.
// When the single page app is loaded further down in this file,
// the correct url will be waiting in the browser's history for
// the single page app to route accordingly.
(function(l) {
    if (l.search[1] === '/' ) {
        var decoded = l.search.slice(1).split('&').map(function(s) { 
        return s.replace(/~and~/g, '&')
        }).join('?');
        window.history.replaceState(null, null,
            l.pathname.slice(0, -1) + decoded + l.hash
        );
    }
}(window.location))
</script>

It navigates to the correct page by setting the state directly using the window.history.replaceState API. This sets the state that Blazor WASM uses internally to keep track of which page you are on.

Conclusion

Now we have seen when the 404 error happens in a Blazor WASM project hosted on GitHub Pages. We have explored why it happens. And in the end, we have seen a solution for the problem which it shares with other SPA frameworks on GitHub Pages.
If you have any questions about the content of this article or comments then feel free to reach out.

elmah.io: Error logging and Uptime Monitoring for your web apps

This blog post is brought to you by elmah.io. elmah.io is error logging, uptime monitoring, deployment tracking, and service heartbeats for your .NET and JavaScript applications. Stop relying on your users to notify you when something is wrong or dig through hundreds of megabytes of log files spread across servers. With elmah.io, we store all of your log messages, notify you through popular channels like email, Slack, and Microsoft Teams, and help you fix errors fast.

See how we can help you monitor your website for crashes Monitor your website