Stale API responses in Blazor WebAssembly HttpClient
0 reputation · 16 Sept 2025, 13:26 UTC
0 reputation · 16 Sept 2025, 13:26 UTC
Blazor WebAssembly applications utilizing HttpClient for data retrieval rely on the browser's native Fetch API. This architecture means that responses are subject to standard HTTP caching headers, such as Cache-Control and ETag.
When a component triggers OnInitializedAsync, the browser may resolve the request internally from the disk or memory cache rather than performing a network round-trip. This leads to the UI displaying stale data even after the application state has theoretically reset.
There is uncertainty regarding the consistent enforcement of Cache-Control: no-cache across different browser engines when these headers are managed via the WASM HttpClient implementation.
HttpClient provide a mechanism to bypass the browser cache without manually appending unique query strings to every request?no-cache versus no-store differ when requests are initiated from the WASM runtime?34775 reputation · 16 Sept 2025, 14:21 UTC
No. Blazor WebAssembly HttpClient does not expose a native API to bypass the browser cache. The WASM HttpClient is a thin wrapper over the browser Fetch API, so caching is controlled by the browser and by HTTP response headers, not by Blazor.
You can avoid query-string workarounds by controlling caching at the HTTP layer:
There is no HttpClient option to set fetch cache mode from WASM code.
The browser may return a response from memory or disk cache for a GET request that is considered fresh. OnInitializedAsync runs again but the fetch is satisfied locally, so the UI shows old data even though the component state reset. This is browser behavior, not a Blazor bug.
Both are request/response headers interpreted by the browser engine. From the WASM runtime there is no difference in how they are sent.
Enforcement is not perfectly consistent across Chromium, WebKit and Gecko, and can be altered by service workers, offline manifests, or intermediate proxies.
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Expires: 0
public class NoCacheHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken ct)
{
request.Headers.CacheControl = new System.Net.Http.Headers.CacheControlHeaderValue { NoCache = true };
return base.SendAsync(request, ct);
}
}
One diagnostic that changes the recommendation: is a service worker registered for the Blazor app? A service worker can intercept fetch and override Cache-Control. If present, header changes alone may not be sufficient and the service worker cache strategy must be updated.
Use comments to ask for clarification. Post a solution as an answer.
6,250 reputation · 16 Sept 2025, 14:12 UTC
Blazor WebAssembly’s HttpClient is a thin wrapper around the browser’s fetch API, so it can’t control caching itself. The simplest way to make every request re‑validate (or never cache) is to add a DelegatingHandler that injects a Cache‑Control header.
public class NoCacheHandler : DelegatingHandler
{
protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.CacheControl = new CacheControlHeaderValue
{
NoCache = true // sends "Cache-Control: no-cache"
// NoStore = true // would send "Cache-Control: no-store"
};
return base.SendAsync(request, cancellationToken);
}
}
Register it with the HttpClientFactory:
builder.Services.AddHttpClient("api", client => client.BaseAddress = new Uri("/api/") )
.AddHttpMessageHandler<NoCacheHandler>();
With this handler the browser always performs a network round‑trip (for no-cache) or never stores the response (for no-store), eliminating stale data without resorting to cache‑busting query strings.