Stop customer firewalls blocking your Blazor WASM DLLs

When a new user first accesses your Blazor WASM website, they can sometimes receive the following unhelpful user experience:

A broken Blazor WebAssembly application

The problem

Large companies often have strict firewall rules to protect themselves from users downloading files that can potentially be harmful to the company’s IT systems. One you’ll experience often is a rule to prohibit the downloading of binary executable files. This includes DLL files as well as EXE files.

Unfortunately, this means that when the user tries to access your Blazor WASM site and your app tries to download all required files, the firewall blocks your DLL files, returns a 403 (Forbidden) response, and your website is broken.

Preventing firewalls from blocking our DLLs

Microsoft have a solution to this posted on their website that uses a custom deployment approach but, to be honest, the post was very long and looked quite complicated, and my gut was telling me to bookmark it and only go back if I couldn’t solve it another way.

When you publish a Blazor WASM site, the current build pipeline will not only deploy the required DLLs, but also GZip and Brotli compressed files too (MyApp.dll.gz and MyApp.dll.br). The purpose of these files is to enable smaller download sizes when uploading to a statically-hosted website.

An ASP.NET hosted website will read headers in the request and see which compression methods the client will accept and then compress responses to improve network times. Statically hosted websites cannot do this, obviously because there is no server, so the DOTNET publish process pre-compiles the files for us and gives us a hook we can intercept in our index.html to download the compressed files instead.

Request headers indicating gzip, deflate, and brotli can be accepted by the browser

So we can achieve our goal more more easily. Instead of writing a custom deployment step, we can simply include a Brotli decompressor script from Google and make a few changes to our index.html and all our problems are solved.

TL;DR – The solution

Note: At the time I first tried this approach for https:://ThePeriodicTableOfElementsGame.com, the Brotli.js file from Google had a bug that stopped this approach from working, so I recommend you get that file from my repo.

First, edit your wwwroot/index.html file and change the blazor.webassembly script so autostart is set to false.

<script src="_framework/blazor.webassembly.js" autostart="false"></script>

Beneath that, reference the Brotli script you downloaded into your wwwroot folder, and add the following content.

    <script src="js/decode.min.js"></script>
    <script>
        Blazor.start({
            loadBootResource: function (type, name, defaultUri, integrity) {
                if (type !== 'dotnetjs' && location.hostname !== 'localhost') {
                    return (async function () {
                        const response = await fetch(defaultUri + '.br',);
                        if (!response.ok) {
                            throw new Error(response.statusText);
                        }
                        const originalResponseBuffer = await response.arrayBuffer();
                        const originalResponseArray = new Int8Array(originalResponseBuffer);
                        const decompressedResponseArray = BrotliDecode(originalResponseArray);
                        const contentType = type ===
                            'dotnetwasm' ? 'application/wasm' : 'application/octet-stream';
                        return new Response(decompressedResponseArray,
                            { headers: { 'content-type': contentType } });
                    })();
                }
            }
        });
    </script>

This script does the following:

  1. Intercepts any attempts by blazor.webassembly.js to download a required resource such as MyApp.dll
  2. Makes an HTTP get for the .br compressed resource instead, e.g. MyApp.dll.br
  3. Decompresses the response using Google’s Brotli library
  4. Returns the decompressed stream to Blazor as if it had requested the original DLL file

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *