Troubleshooting Blazor WebAssembly Deployment Issues
Your Blazor WebAssembly app runs flawlessly with dotnet run. You publish it to IIS, browse to the URL, and get a blank white page — or a wall of red 404s in the console for _framework/*.wasm, or a cryptic Failed to find a valid digest in the 'integrity' attribute. Blazor WASM deployment failures are almost always configuration, not code: the browser downloads a self-contained .NET runtime plus your assemblies as static files, and the web server has to serve every one of them with the right MIME type, compression, and fallback routing. This is the field guide we use to diagnose Blazor WebAssembly deployments on Adaptive's Windows + IIS hosting — nine issues in order of frequency, each with the symptom, the diagnostic, and the fix.
WASMStandalone & Hosted
.NET 10LTS — updated for
IIS 10Pre-tuned for Blazor
One distinction up front, because half of all Blazor WASM deployment confusion comes from it: a standalone WebAssembly app is pure static files (HTML, JS, .wasm) served by any web server — it needs MIME types and URL-rewrite fallback. A hosted app ships an ASP.NET Core server project that serves the client and usually an API — it handles fallback routing in code. Knowing which one you deployed determines half the fixes below. If you're still choosing, the Blazor Server vs WebAssembly hosting guide compares the trade-offs.
CAUSE GRID -->
Match the symptom to the cause
Start here. The visible symptom usually points straight at the culprit:
404 on _framework/*.wasm
Missing MIME type or an incomplete publish. The runtime never loads. → Issues #1, #7
🔴 Integrity error
"valid digest in 'integrity'"
blazor.boot.json hashes don't match served bytes. → Issue #4
🟠 404 on refresh
F5 on a deep route fails
No URL-rewrite fallback to index.html. → Issue #3
🟠 Assets 404
Everything broken under a sub-path
Wrong <base href> for the deploy path. → Issue #2
🟠 Slow first load
Multi-MB download every time
Precompressed .br/.gz not being served. → Issue #5
🟡 Stale build
Old version keeps loading
Service worker or boot.json over-cached. → Issues #6, #9
Quick reference: the 9 issues
- Missing MIME Types for WASM Assets
Blank page. Console shows 404 (Not Found) or Failed to load resource for files under _framework/, or a refusal to execute because the .wasm was served as text/plain. IIS returns 404 for any extension it doesn't have a MIME map for.
Diagnostic: Open DevTools → Network, reload, and look at the failing requests. If dotnet.native.wasm or a .dat file returns 404 or the wrong Content-Type, this is your issue.
Fix: IIS 10 maps application/wasm for .wasm out of the box, but custom or locked-down configs and the Blazor data files often need explicit maps. The SDK-published web.config includes them — if yours is missing or was overwritten, add them back:
<system.webServer>
<staticContent>
<remove fileExtension=".wasm" />
<mimeMap fileExtension=".wasm" mimeType="application/wasm" />
<remove fileExtension=".dat" />
<mimeMap fileExtension=".dat" mimeType="application/octet-stream" />
<remove fileExtension=".blat" />
<mimeMap fileExtension=".blat" mimeType="application/octet-stream" />
<remove fileExtension=".json" />
<mimeMap fileExtension=".json" mimeType="application/json" />
</staticContent>
</system.webServer>
Since .NET 8, Blazor WebAssembly packages your assemblies as Webcil — they ship as .wasm files instead of .dll. That eliminated the classic problem of antivirus and corporate proxies blocking .dll downloads and the need to map .dll as a MIME type. If you've explicitly disabled Webcil (<WasmEnableWebcil>false</WasmEnableWebcil>), you'll still need the old .dll → application/octet-stream map. On .NET 10, leave Webcil on.
- Wrong <base href> for the Deployment Path
The app works at the site root but every asset 404s when deployed into a sub-application like https://example.com/myapp/. The browser requests /_framework/... at the domain root instead of /myapp/_framework/....
Blazor resolves every asset relative to the <base href> in wwwroot/index.html. A root deployment needs "/"; a sub-application needs the virtual path with leading and trailing slashes.
Fix: Set it to match where the app actually lives:
<!-- Deployed at the domain root -->
<base href="/" />
<!-- Deployed as an IIS sub-application at /myapp -->
<base href="/myapp/" />
You can override it at publish time without editing the file, which is cleaner for multi-environment pipelines:
dotnet publish -c Release -p:StaticWebAssetBasePath=myapp
- Deep-Link Refresh Returns 404
Navigating inside the app works, but pressing F5 or pasting a deep link like /orders/5 returns an IIS 404. There's no orders/5 file on disk — Blazor's router is supposed to handle that route client-side, but the request reaches IIS first.
This is the defining trait of a single-page app. The server must serve index.html for any path that isn't a real file, then Blazor's router takes over. Standalone WASM relies on an IIS URL Rewrite rule; a hosted app does it in the server project.
Fix (standalone, IIS): the SDK's published web.config includes this rule — restore it if it's missing:
<system.webServer>
<rewrite>
<rules>
<rule name="Blazor SPA fallback" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="index.html" />
</rule>
</rules>
</rewrite>
</system.webServer>
Fix (hosted): the server project does it in Program.cs:
app.MapFallbackToFile("index.html");
The rewrite rule depends on the IIS URL Rewrite module — a standard component on Windows .NET hosting, including Adaptive's IIS 10 environment. If a rule silently does nothing, confirm the module is present; without it, IIS ignores the <rewrite> section entirely.
- Integrity / Digest Check Failure
Failed to find a valid digest in the 'integrity' attribute for resource '...' with computed SHA-256 integrity .... Blazor verifies every downloaded file against the SHA hashes recorded in blazor.boot.json; a mismatch aborts the boot.
The bytes the browser received don't match what was published. Common causes: a proxy or CDN re-compressing or modifying files in flight, a partial/overwritten deploy where some files are new and some are old, or aggressive caching serving a stale blazor.boot.json against fresh assets (or vice versa).
Diagnostic: Hard-refresh with the cache disabled (DevTools → Network → "Disable cache"). If it boots clean, you have a caching mismatch (see issue #9). If it still fails, something is altering the files between publish and the browser.
Fix:
Do a clean redeploy — delete the target folder first so no stale file survives. A half-overwritten _framework is the #1 cause.
Ensure no proxy/CDN is transforming responses on the fly. Serve the SDK's own precompressed files rather than letting an intermediary re-encode them.
Send blazor.boot.json with Cache-Control: no-cache so the manifest and the assets it describes never drift apart.
- Precompressed Assets Not Being Served
First load downloads several megabytes and feels slow, even though dotnet publish produced .br and .gz copies of every asset. IIS is serving the uncompressed originals because nothing tells it to prefer the Brotli versions.
Blazor's publish step pre-Brotli-compresses the runtime and assemblies. Serving the .br file to clients that accept Brotli can cut the transfer by 60–75%. The catch: IIS won't do it automatically — you add a rewrite that swaps in the .br file and sets the right headers.
Fix: serve the precompressed file when the client supports it:
<rewrite>
<rules>
<rule name="Serve Brotli" stopProcessing="true">
<match url="(.*)\.(wasm|js|dll|dat|json)$" />
<conditions>
<add input="{HTTP_ACCEPT_ENCODING}" pattern="br" />
<add input="{REQUEST_FILENAME}.br" matchType="IsFile" />
</conditions>
<action type="Rewrite" url="{R:0}.br" />
</rule>
</rules>
<outboundRules>
<rule name="Brotli header">
<match serverVariable="RESPONSE_Content_Encoding" pattern=".*" />
<action type="Rewrite" value="br" />
</rule>
</outboundRules>
</rewrite>
Compression is the single biggest win for Blazor WASM startup, but it's not the only one. Lazy-loading assemblies, trimming, and runtime relinking matter too — the Blazor WebAssembly performance guide covers all twelve. For the most aggressive size and speed wins, Native AOT on .NET 10 is worth a look.
- Service Worker Caches the Old Build (PWA)
You deploy a new version, but users keep seeing the old one until they hard-refresh — or report bugs you already fixed. This only happens to Progressive Web App Blazor projects, which register a service worker that caches the whole app for offline use.
A PWA service worker serves the cached app first, then checks for updates in the background. By design, the new version activates on the next visit, not the current one — so a single refresh isn't enough.
Fix: the published service-worker-assets.js carries a content hash per asset; as long as you deploy the regenerated file, the worker detects the change. To make updates land faster, prompt users when a new version is waiting, or skip the wait:
// In service-worker.published.js — activate the new worker immediately
self.addEventListener('install', e => e.waitUntil(self.skipWaiting()));
self.addEventListener('activate', e => e.waitUntil(self.clients.claim()));
skipWaiting updates faster but can swap assets under a user mid-session. The safer pattern is to detect the waiting worker and show a "New version available — reload" toast, letting the user choose the moment. Only enable a PWA if you actually need offline support; many Blazor apps don't.
- Incomplete Publish or Corrupted Upload
The _framework folder is missing entirely, or some files are zero bytes, or binaries are subtly corrupted. Often the result of deploying the build output instead of the publish output, or an FTP client uploading in ASCII mode and mangling binary files.
Blazor WASM must be published, not just built — the publish step is what assembles the self-contained wwwroot with the runtime, Webcil assemblies, and compressed copies. Copying bin/Release/... straight to the server skips all of it.
Fix:
Always publish, in Release, to a clean folder
dotnet publish -c Release -o ./publish
Standalone: deploy the CONTENTS of ./publish/wwwroot
Hosted: deploy ./publish (the server app, which contains wwwroot)
If you upload over FTP, force binary transfer mode — ASCII mode silently corrupts .wasm and .dat files and produces baffling integrity errors. Better yet, use Web Deploy: the Web Deploy from Visual Studio guide and the deploy to IIS with Plesk walkthrough both avoid the whole class of upload-corruption problems.
- Hosted vs Standalone Mismatch
API calls 404 in production even though the UI loads, or the SPA fallback rule does nothing because there's actually an ASP.NET Core server expecting to handle routing. You deployed the wrong half of a hosted solution — or treated a hosted app like a static site.
Decide which model you have, because the deployment differs completely:
- blazor.boot.json Over-Cached
A fresh deploy never appears for returning visitors, or returning users hit issue #4's integrity error because the browser holds an old blazor.boot.json that references files no longer on the server.
blazor.boot.json is the manifest that lists every runtime file and its hash. If a proxy or the browser caches it too aggressively, the app boots against a stale view of the world. The fingerprinted assets it points to can be cached forever; the manifest itself must not be.
Fix: never cache the manifest, cache the hashed assets immutably:
<location path="_framework/blazor.boot.json">
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Cache-Control" value="no-cache, no-store, must-revalidate" />
</customHeaders>
</httpProtocol>
</system.webServer>
</location>
.NET 8 introduced content-hash fingerprinting for Blazor assets, so the runtime files carry their hash in the URL and are safe to cache with a long max-age, immutable. The manifest stays the one file you keep on a short leash — that pairing gives you fast repeat loads and instant updates.
The diagnostic flow we recommend
Open DevTools → Console. A digest/integrity message → issue #4 or #9. A 404 for _framework files → issue #1 or #7.
Switch to the Network tab, reload with "Disable cache" on. If it works clean, the problem is caching (#9 / #6). If a .wasm shows the wrong Content-Type or 404, it's MIME (#1).
Check whether assets are requested at the right path. Requests to / when the app lives at /myapp/ → wrong <base href> (#2).
Press F5 on a deep route. A 404 means the SPA fallback rewrite is missing (#3).
Confirm you deployed the publish output (with _framework populated), over a binary-safe transport (#7), and that you deployed the correct project for your model (#8).
Reference web.config for Blazor WASM on IIS
For a standalone Blazor WebAssembly app on IIS, this web.config in the deployed root covers MIME types, SPA fallback, and manifest caching in one place. The SDK generates most of it on publish — keep this as your known-good baseline:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<staticContent>
<remove fileExtension=".wasm" />
<mimeMap fileExtension=".wasm" mimeType="application/wasm" />
<remove fileExtension=".dat" />
<mimeMap fileExtension=".dat" mimeType="application/octet-stream" />
<remove fileExtension=".blat" />
<mimeMap fileExtension=".blat" mimeType="application/octet-stream" />
</staticContent>
<rewrite>
<rules>
<rule name="Blazor SPA fallback" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="index.html" />
</rule>
</rules>
</rewrite>
</system.webServer>
<location path="_framework/blazor.boot.json">
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Cache-Control" value="no-cache, no-store, must-revalidate" />
</customHeaders>
</httpProtocol>
</system.webServer>
</location>
</configuration>
Blazor WASM deployment checklist
✅ IIS Configuration
MIME maps for .wasm, .dat, .blat
SPA fallback rewrite to index.html (standalone)
URL Rewrite module present
ASP.NET Core hosting bundle installed (hosted only)
✅ Caching & Compression
blazor.boot.json served no-cache
Fingerprinted assets cached immutable
Brotli/gzip precompressed files actually served
Service worker update flow handled (PWA only)
✅ Verification
Hard-refresh boots with no console errors
F5 on a deep route loads (no 404)
Network tab shows .br served, correct MIME
A redeploy is visible without a manual cache clear
How Adaptive Web Hosting's IIS Is Tuned for Blazor WASM
The most common Blazor WebAssembly deployment failures come down to IIS not being set up for static SPA workloads — missing MIME maps, no URL Rewrite, no compression. Adaptive's Blazor WebAssembly hosting runs on Windows Server 2022 with IIS 10 configured for exactly this:
IIS 10 with WASM MIME support — application/wasm and the Blazor data types served correctly out of the box
URL Rewrite module available — the SPA fallback rule the SDK generates just works
Static compression — your precompressed .br/.gz assets are served, so first load stays fast
ASP.NET Core hosting bundle — for hosted Blazor WASM, the ANCM handler is installed and ready
Dedicated IIS Application Pool — your app isn't fighting neighbours for memory or CPU
Plesk control panel — manage MIME types, rewrite rules, and SSL without RDP
FREE SSL on every site — Blazor WASM needs HTTPS for service workers and the secure context anyway
Choose a plan
$9.49/mo
Standalone Blazor WASM apps, staging, and smaller projects. 1 GB dedicated app pool, free SSL.
See Developer plan features →
Popular
ASP.NET Business
$17.49/mo
Hosted Blazor WASM with an API backend. 2 GB dedicated app pool, real SQL Server, WAF.
See Business plan features →
ASP.NET Professional
$27.49/mo
Agencies shipping multiple Blazor apps. 10 sites, 200 GB SSD, 4 GB dedicated app pool.
See Professional plan features →
Frequently Asked Questions
Why does my Blazor WebAssembly app show a blank white page on IIS?
Almost always a missing MIME type (issue #1) or an incomplete publish (issue #7). Open DevTools → Console and Network: a 404 for files under _framework/ means IIS can't serve the runtime, usually because application/wasm or a .dat map is missing, or you deployed the build output instead of the publish output. Add the MIME maps and redeploy the publish folder.
Do I still need a .dll MIME type for Blazor WASM in .NET 10?
No, as long as you're on the default. Since .NET 8, Blazor packages assemblies as Webcil .wasm files, so there's no .dll going over the wire and no .dll MIME map needed. You'd only need it if you explicitly set <WasmEnableWebcil>false</WasmEnableWebcil>, which is rare and not recommended on .NET 10.
How do I fix a 404 when refreshing a Blazor route?
Add the SPA fallback so the server returns index.html for any non-file path (issue #3). Standalone WASM uses an IIS URL Rewrite rule; hosted apps call app.MapFallbackToFile("index.html") in Program.cs. The standalone rule ships in the SDK-generated web.config — if deep-link refresh 404s, that rule is missing or the URL Rewrite module isn't active.
Why do I get an integrity error after deploying a new version?
The bytes the browser downloaded don't match the SHA hashes in blazor.boot.json (issue #4). The usual causes are a half-overwritten deploy, a proxy re-compressing files in flight, or a stale cached manifest. Do a clean redeploy to an empty folder, serve blazor.boot.json with no-cache, and make sure nothing between IIS and the browser is transforming the files.
Should I deploy Blazor Server or Blazor WebAssembly?
WebAssembly runs entirely in the browser (great for static hosting, offline, and CDN distribution, at the cost of a larger first load). Server keeps the app on the host over a SignalR circuit (instant load, but a live connection per user). The Blazor Server vs WebAssembly hosting guide walks through the decision, and if you picked Server, the SignalR connection-dropping guide covers its main production gotcha.
Does Adaptive Web Hosting support Blazor WebAssembly?
Yes — both standalone and hosted. Blazor WebAssembly hosting runs on IIS 10 with WASM MIME types, the URL Rewrite module, and static compression already configured, so the deployment issues in this article are largely pre-solved. Hosted apps get the ASP.NET Core hosting bundle for the server project. See the full Blazor hosting guide for Server, WASM, and Auto.
How do I serve Brotli-compressed Blazor files on IIS?
Blazor's publish step already creates .br and .gz copies; IIS just needs to prefer them. Add a URL Rewrite rule that swaps in the .br file when the request's Accept-Encoding includes br and sets Content-Encoding: br (issue #5). On Adaptive's IIS this is in place, but if you self-manage IIS, the rewrite rule above is the standard approach.
Bottom line
Blazor WebAssembly deployment failures look alarming — a blank page, a wall of 404s, a cryptic integrity error — but they trace back to a short list of configuration gaps: MIME types, <base href>, SPA fallback routing, compression, and caching. Work the symptom-to-cause grid, confirm you published (not built) and deployed binary-safe, and the runtime boots. The reference web.config above resolves the majority of cases on its own.
On Adaptive Web Hosting, IIS 10 ships configured for static SPA workloads — WASM MIME types, URL Rewrite, and compression already in place — so most of these issues never surface in the first place. ASP.NET Developer ($9.49/mo) suits standalone WASM and staging, ASP.NET Business ($17.49/mo) fits hosted WASM with an API and real SQL Server, and ASP.NET Professional ($27.49/mo) is built for agencies shipping multiple apps. Every plan includes a 30-day money-back guarantee. View Blazor hosting plans, brush up on WASM performance optimization, or talk to an engineer about a specific deployment.