Skip to content
Shiny.Net.HttpServer v1 - A lightweight feature rich HTTP Server - Tunnels, Websockets, AOT, ASPNET Featureset, & Works EVERYWHERE!Let me see!

Blazor WebAssembly

A Blazor WebAssembly app is a folder of static files, so nothing is special-cased for its sake. This is the static file handler with the three settings a Blazor publish needs — and getting any of them wrong produces a blank page and a stack trace from inside the runtime rather than an obvious error.

app.UseBlazorWebAssembly("./wwwroot");
app.MapGet("/api/readings", …); // the app's own API, alongside it

For the packaged case — a MAUI or single-file build with no wwwroot on disk:

app.UseBlazorWebAssembly(typeof(App).Assembly, "MyApp.wwwroot");

The single-page fallback. A Blazor route like /counter exists only in the client-side router, so a reload of that URL has to return index.html. The fallback is still restricted to requests that look like navigations, so a missing .js 404s honestly.

Precompressed assets. A publish emits .br and .gz beside every file, compressed once at maximum effort. Serving those beats recompressing 20-odd megabytes per request at a level chosen for speed. ServePrecompressedFiles is on, and the disk source is configured to look for br then gzip.

Cache policy. Everything under _framework and _content is content-fingerprinted by the publish, so it is served public, max-age=31536000, immutable. The entry document is no-cache — it keeps the same URL by definition, and caching it is how a deploy silently fails to reach anyone. no-cache rather than no-store: the browser may keep its copy, it just has to revalidate, and with an ETag that is a conditional request answered by a 304.

You can still adjust everything through the optional configure callback, which runs after those defaults are applied.

Only one thing was ever actually missing when this was verified against a real publish — 26 MB of assets and 36 wasm assemblies, loaded in Chrome and interactive. .dat, the ICU globalization data, had no content type, and an unknown extension is not served, so the runtime died on a 404 before it started.

.dat, .blat, .webcil, .dll, .pdb and .webmanifest are in the built-in map. If a publish ever emits something else new, add it rather than turning on ServeUnknownFileTypes:

app.UseBlazorWebAssembly("./wwwroot", o => o.ContentTypeOverrides[".xyz"] = "application/octet-stream");

The interesting deployment is not a web server. It is a Blazor app served by the device it talks to: a MAUI app hosting its own UI, an appliance with no cloud, a piece of equipment whose configuration page is only reachable on the LAN. Pair it with a tunnel and the same app is reachable from anywhere without any of it being deployed anywhere.