Bewerken

Delen via


ASP.NET Core Blazor WebAssembly .NET runtime and app bundle caching

Note

This isn't the latest version of this article. For the current release, see the .NET 8 version of this article.

Warning

This version of ASP.NET Core is no longer supported. For more information, see .NET and .NET Core Support Policy. For the current release, see the .NET 8 version of this article.

Important

This information relates to a pre-release product that may be substantially modified before it's commercially released. Microsoft makes no warranties, express or implied, with respect to the information provided here.

For the current release, see the .NET 8 version of this article.

When a Blazor WebAssembly app loads in the browser, the app downloads boot resources from the server:

  • JavaScript code to bootstrap the app
  • .NET runtime and assemblies
  • Locale specific data

Except for Blazor's boot resources file (blazor.boot.json), WebAssembly .NET runtime and app bundle files are cached on clients. The blazor.boot.json file contains a manifest of the files that make up the app that must be downloaded along with a hash of the file's content that's used to detect whether any of the boot resources have changed. Blazor caches downloaded files using the browser Cache API.

When Blazor WebAssembly downloads an app's startup files, it instructs the browser to perform integrity checks on the responses. Blazor sends SHA-256 hash values for DLL (.dll), WebAssembly (.wasm), and other files in the blazor.boot.json file. The file hashes of cached files are compared to the hashes in the blazor.boot.json file. For cached files with a matching hash, Blazor uses the cached files. Otherwise, files are requested from the server. After a file is downloaded, its hash is checked again for integrity validation. An error is generated by the browser if any downloaded file's integrity check fails.

Blazor's algorithm for managing file integrity:

  • Ensures that the app doesn't risk loading an inconsistent set of files, for example if a new deployment is applied to your web server while the user is in the process of downloading the application files. Inconsistent files can result in a malfunctioning app.
  • Ensures the user's browser never caches inconsistent or invalid responses, which can prevent the app from starting even if the user manually refreshes the page.
  • Makes it safe to cache the responses and not check for server-side changes until the expected SHA-256 hashes themselves change, so subsequent page loads involve fewer requests and complete faster.

If the web server returns responses that don't match the expected SHA-256 hashes, an error similar to the following example appears in the browser's developer console:

Failed to find a valid digest in the 'integrity' attribute for resource 'https://myapp.example.com/_framework/MyBlazorApp.dll' with computed SHA-256 integrity 'IIa70iwvmEg5WiDV17OpQ5eCztNYqL186J56852RpJY='. The resource has been blocked.

In most cases, the warning doesn't indicate a problem with integrity checking. Instead, the warning usually means that some other problem exists.

Note

Documentation links to .NET reference source usually load the repository's default branch, which represents the current development for the next release of .NET. To select a tag for a specific release, use the Switch branches or tags dropdown list. For more information, see How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205).

Diagnosing integrity problems

When an app is built, the generated blazor.boot.json manifest describes the SHA-256 hashes of boot resources at the time that the build output is produced. The integrity check passes as long as the SHA-256 hashes in blazor.boot.json match the files delivered to the browser.

Common reasons why this fails include:

  • The web server's response is an error (for example, a 404 - Not Found or a 500 - Internal Server Error) instead of the file the browser requested. This is reported by the browser as an integrity check failure and not as a response failure.
  • Something has changed the contents of the files between the build and delivery of the files to the browser. This might happen:
    • If you or build tools manually modify the build output.
    • If some aspect of the deployment process modified the files. For example if you use a Git-based deployment mechanism, bear in mind that Git transparently converts Windows-style line endings to Unix-style line endings if you commit files on Windows and check them out on Linux. Changing file line endings change the SHA-256 hashes. To avoid this problem, consider using .gitattributes to treat build artifacts as binary files.
    • The web server modifies the file contents as part of serving them. For example, some content distribution networks (CDNs) automatically attempt to minify HTML, thereby modifying it. You may need to disable such features.
  • The blazor.boot.json file fails to load properly or is improperly cached on the client. Common causes include either of the following:
    • Misconfigured or malfunctioning custom developer code.
    • One or more misconfigured intermediate caching layers.

To diagnose which of these applies in your case:

  1. Note which file is triggering the error by reading the error message.
  2. Open your browser's developer tools and look in the Network tab. If necessary, reload the page to see the list of requests and responses. Find the file that is triggering the error in that list.
  3. Check the HTTP status code in the response. If the server returns anything other than 200 - OK (or another 2xx status code), then you have a server-side problem to diagnose. For example, status code 403 means there's an authorization problem, whereas status code 500 means the server is failing in an unspecified manner. Consult server-side logs to diagnose and fix the app.
  4. If the status code is 200 - OK for the resource, look at the response content in browser's developer tools and check that the content matches up with the data expected. For example, a common problem is to misconfigure routing so that requests return your index.html data even for other files. Make sure that responses to .wasm requests are WebAssembly binaries and that responses to .dll requests are .NET assembly binaries. If not, you have a server-side routing problem to diagnose.
  5. Seek to validate the app's published and deployed output with the Troubleshoot integrity PowerShell script.

If you confirm that the server is returning plausibly correct data, there must be something else modifying the contents in between build and delivery of the file. To investigate this:

  • Examine the build toolchain and deployment mechanism in case they're modifying files after the files are built. An example of this is when Git transforms file line endings, as described earlier.
  • Examine the web server or CDN configuration in case they're set up to modify responses dynamically (for example, trying to minify HTML). It's fine for the web server to implement HTTP compression (for example, returning content-encoding: br or content-encoding: gzip), since this doesn't affect the result after decompression. However, it's not fine for the web server to modify the uncompressed data.

Troubleshoot integrity PowerShell script

Use the integrity.ps1 PowerShell script to validate a published and deployed Blazor app. The script is provided for PowerShell Core 7 or later as a starting point when the app has integrity issues that the Blazor framework can't identify. Customization of the script might be required for your apps, including if running on version of PowerShell later than version 7.2.0.

The script checks the files in the publish folder and downloaded from the deployed app to detect issues in the different manifests that contain integrity hashes. These checks should detect the most common problems:

  • You modified a file in the published output without realizing it.
  • The app wasn't correctly deployed to the deployment target, or something changed within the deployment target's environment.
  • There are differences between the deployed app and the output from publishing the app.

Invoke the script with the following command in a PowerShell command shell:

.\integrity.ps1 {BASE URL} {PUBLISH OUTPUT FOLDER}

In the following example, the script is executed on a locally-running app at https://localhost:5001/:

.\integrity.ps1 https://localhost:5001/ C:\TestApps\BlazorSample\bin\Release\net6.0\publish\

Placeholders:

  • {BASE URL}: The URL of the deployed app. A trailing slash (/) is required.
  • {PUBLISH OUTPUT FOLDER}: The path to the app's publish folder or location where the app is published for deployment.

Note

When cloning the dotnet/AspNetCore.Docs GitHub repository, the integrity.ps1 script might be quarantined by Bitdefender or another virus scanner present on the system. Usually, the file is trapped by a virus scanner's heuristic scanning technology, which merely looks for patterns in files that might indicate the presence of malware. To prevent the virus scanner from quarantining the file, add an exception to the virus scanner prior to cloning the repo. The following example is a typical path to the script on a Windows system. Adjust the path as needed for other systems. The {USER} placeholder is the user's path segment.

C:\Users\{USER}\Documents\GitHub\AspNetCore.Docs\aspnetcore\blazor\host-and-deploy\webassembly\_samples\integrity.ps1

Warning: Creating virus scanner exceptions is dangerous and should only be performed when you're certain that the file is safe.

Comparing the checksum of a file to a valid checksum value doesn't guarantee file safety, but modifying a file in a way that maintains a checksum value isn't trivial for malicious users. Therefore, checksums are useful as a general security approach. Compare the checksum of the local integrity.ps1 file to one of the following values:

  • SHA256: 32c24cb667d79a701135cb72f6bae490d81703323f61b8af2c7e5e5dc0f0c2bb
  • MD5: 9cee7d7ec86ee809a329b5406fbf21a8

Obtain the file's checksum on Windows OS with the following command. Provide the path and file name for the {PATH AND FILE NAME} placeholder and indicate the type of checksum to produce for the {SHA512|MD5} placeholder, either SHA256 or MD5:

CertUtil -hashfile {PATH AND FILE NAME} {SHA256|MD5}

If you have any cause for concern that checksum validation isn't secure enough in your environment, consult your organization's security leadership for guidance.

For more information, see Overview of threat protection by Microsoft Defender Antivirus.

Disable resource caching and integrity checks for non-PWA apps

In most cases, don't disable integrity checking. Disabling integrity checking doesn't solve the underlying problem that has caused the unexpected responses and results in losing the benefits listed earlier.

There may be cases where the web server can't be relied upon to return consistent responses, and you have no choice but to temporarily disable integrity checks until the underlying problem is resolved.

To disable integrity checks, add the following to a property group in the Blazor WebAssembly app's project file (.csproj):

<BlazorCacheBootResources>false</BlazorCacheBootResources>

BlazorCacheBootResources also disables Blazor's default behavior of caching the .dll, .wasm, and other files based on their SHA-256 hashes because the property indicates that the SHA-256 hashes can't be relied upon for correctness. Even with this setting, the browser's normal HTTP cache may still cache those files, but whether or not this happens depends on your web server configuration and the cache-control headers that it serves.

Note

The BlazorCacheBootResources property doesn't disable integrity checks for Progressive Web Applications (PWAs). For guidance pertaining to PWAs, see the Disable integrity checking for PWAs section.

We can't provide an exhaustive list of scenarios where disabling integrity checking is required. Servers can answer a request in arbitrary ways outside of the scope of the Blazor framework. The framework provides the BlazorCacheBootResources setting to make the app runnable at the cost of losing a guarantee of integrity that the app can provide. Again, we don't recommend disabling integrity checking, especially for production deployments. Developers should seek to solve the underlying integrity problem that's causing integrity checking to fail.

A few general cases that can cause integrity issues are:

  • Running on HTTP where integrity can't be checked.
  • If your deployment process modifies the files after publish in any way.
  • If your host modifies the files in any way.

Disable resource caching and integrity checks for PWAs

Blazor's Progressive Web Application (PWA) template contains a suggested service-worker.published.js file that's responsible for fetching and storing application files for offline use. This is a separate process from the normal app startup mechanism and has its own separate integrity checking logic.

Inside the service-worker.published.js file, following line is present:

.map(asset => new Request(asset.url, { integrity: asset.hash }));

To disable integrity checking, remove the integrity parameter by changing the line to the following:

.map(asset => new Request(asset.url));

Again, disabling integrity checking means that you lose the safety guarantees offered by integrity checking. For example, there is a risk that if the user's browser is caching the app at the exact moment that you deploy a new version, it could cache some files from the old deployment and some from the new deployment. If that happens, the app becomes stuck in a broken state until you deploy a further update.

Additional resources

Boot resource loading