How to fix Blazor WASM base path problems

A lot of people were intrigued when we got to know that Blazor WASM would come with features like client-side routing. But Blazor WASM applications are still Single Page Applications (SPAs) at their core. This means that they have the same restrictions as any other SPA framework. In this post, I'll show you how I fixed a common problem setting the application base path with Blazor.

How to fix Blazor WASM base path problems

The base path problem

Many might have seen the following error message when publishing a Blazor WASM project.

⛔ Failed to load resource: the server responded with a status of 404 () blazor.webassembly.js:1

This is often because the project has been published to a subdirectory on a domain. This is commonly seen when publishing to GitHub Pages or if you use a subdirectory on your own domain. The immediate response for most developers is a quick online search where you will find people that suggest setting the base path in wwwwroot/index.html.

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <title>testBlazorWebAssembly</title>
    <base href="/" />
    <!-- scripts and resources omitted -->
</head>

The quick fix is to change the base path to the directory that the application is hosted in like so:

<base href="/MyHostedDirectory/" />

With the joy of having solved yet another problem, you continue to develop on your local machine and try to debug the application locally. You meet a familiar error message again:

⛔ Failed to load resource: the server responded with a status of 404 () blazor.webassembly.js:1

This didn't happen before, but that's because we now have a base path that only works when we publish. The base path is what determines the top-level directory for the application. This influence what paths like _framework/blazor.webassembly.js gets interpreted as e.g. one of these:

  1. https://example.com/_framework/blazor.webassembly.js

  2. https://example.com/MyHostedDirectory/_framework/blazor.webassembly.js

or when we debug on localhost:

  1. https://localhost:44444/_framework/blazor.webassembly.js

  2. https://localhost:44444/MyHostedDirectory/_framework/blazor.webassembly.js

The solution

What we wish is to set the base path depending on where we host. Let's do this as general as possible so that this solution can be reused. A solution is adding the following instead of the existing base tag:

<base />
<script>
    var path = window.location.pathname.split('/');
    var base = document.getElementsByTagName('base')[0];
    if (window.location.host.includes('localhost')) {
        base.setAttribute('href', '/');
    } else if (path.length > 2) {
        base.setAttribute('href', '/' + path[1] + '/');
    } else if (path[path.length - 1].length != 0) {
        window.location.replace(window.location.origin + window.location.pathname + '/' + window.location.search);
    }
</script>

The change of removing the href attribute by default and adding it dynamically with JavaScript covers a lot of use cases.

https://example.com/MyHostedDirectory

In this case, the base path is not set, but there is performed a redirect to the same address, but with a slash afterward which is the next case.

https://example.com/MyHostedDirectory/

In this case, the base path is set to "/MyHostedDirectory/" and your resources are fetched correctly

https://example.com/MyHostedDirectory/ARoute

If the SPA uses routing for different pages this script still works and only sets the base path equal to the first part of the path, so the base path is still set correctly to "/MyHostedDirectory/".

https://example.com/MyHostedDirectory?query=fieldString

The script also works if you use a query string since the query string is re-appended if we redirect the user and the query string isn't influencing the base path since query strings are not included in window.location.pathname.

https://localhost:44444/

If we try to test it on localhost again, the first if-statement is valid and we set the base path to "/". Then all of the rest works as we are used to and query strings and routes will work as well.

Closing remarks

The last check to see if there is a slash at the end of the path is not strictly necessary, but greatly improves the user experience by accepting that users might not always add the ending slash.

Conclusions

In this article, we have inspected the common base path problem and its cause. We have pointed to a quick fix and the problems with this fix. In the end, we have presented a more complete solution that should cover most cases for a SPA like Blazor WASM that is hosted in a subdirectory.

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