While working on a .NET 10 Blazor Web App locally using JetBrains Rider’s Docker Fast Mode, I ran into a puzzling bug: the project built without errors, the container started fine, and styles and custom scripts were served properly. However, the core runtime script consistently failed with a 404:
HTTP GET /_framework/blazor.web.js responded 404 in 24.2391 ms
At the same time, every other static file (app.css, Scoped CSS bundles, third-party library assets) returned a clean 200 OK.
My initial thought was that Rider’s Fast Mode had misconfigured the volume mounts or lost system paths inside the container. But the reality turned out to be far more nuanced: the root cause was tied to architectural changes in .NET 10 combined with a recent project refactoring.
How Rider Fast Mode Works
There are two primary ways to run Docker builds during development:
- Standard Docker Build — The container is built via a multi-stage
Dockerfile(FROM ... AS build), executingdotnet restore,dotnet build, anddotnet publishentirely inside the Linux container environment. - Rider Docker Fast Mode — Optimizes the inner dev loop. Rider avoids rebuilding the entire container on every change:
- The base image is built only up to the
basestage (the minimal runtime layer). - Debug DLLs are compiled by the host SDK (in this case, on Windows).
- Project directories (
/bin/Debug,/obj, source files, and the local NuGet cache) are bind-mounted into the container:volumes: - C:\...\MyWebApp\MyWebApp:/app - C:\...\MyWebApp:/src - C:\Users\<User>\.nuget\packages:/home/app/.nuget/packages - An environment variable pointing to the host’s runtime asset manifest is injected into the container:
ASPNETCORE_STATICWEBASSETS=/app/bin/Debug/net10.0/MyWebApp.staticwebassets.runtime.CT.json
- The base image is built only up to the
The application startup invokes app.MapStaticAssets(). Unlike the legacy app.UseStaticFiles(), this endpoint-based middleware does not scan the disk on every incoming request. Instead, it registers endpoints strictly at startup based on the provided JSON manifest.
Inspecting MyWebApp.staticwebassets.endpoints.json on the host revealed:
- Routes for
wwwroot/**were present. - Generated Scoped CSS files were present.
_framework/blazor.web.jsand_framework/blazor.server.jswere completely missing from the manifest.
This ruled out Docker Fast Mode as the culprit. The file was simply never registered in the asset manifest in the first place.
Where Do Blazor Scripts Come From?
Prior to .NET 10, framework scripts (blazor.server.js, blazor.web.js, etc.) were shipped as Embedded Resources inside assemblies like Microsoft.AspNetCore.Components.Server.dll or the shared framework. The runtime extracted them on the fly.
In .NET 10, the ASP.NET Core team moved Blazor framework scripts to the Static Web Assets (SWA) pipeline:
blazor.web.jsandblazor.server.jsare no longer embedded in binary DLLs.- They are distributed via a dedicated internal package:
Microsoft.AspNetCore.App.Internal.Assets. - This change allows framework JS scripts to benefit from the modern static asset pipeline: automatic pre-compression (gzip / Brotli), fingerprinting for cache busting, and delivery through the lightweight
app.MapStaticAssets()endpoint.
blazor.web.js is neither generated by the C# compiler nor located in the project’s physical wwwroot folder. It is supplied through Microsoft.AspNetCore.App.Internal.Assets, which defines the _AddBlazorFrameworkStaticWebAssets MSBuild target:
<Target Name="_AddBlazorFrameworkStaticWebAssets"
Condition="'$(OutputType)' == 'Exe'">
<ItemGroup>
<_FrameworkStaticWebAssetCandidate Include="$(BlazorFrameworkStaticWebAssetRoot)\blazor.web.js">
<RelativePath>_framework/blazor.web.js</RelativePath>
</_FrameworkStaticWebAssetCandidate>
<_FrameworkStaticWebAssetCandidate Include="$(BlazorFrameworkStaticWebAssetRoot)\blazor.server.js">
<RelativePath>_framework/blazor.server.js</RelativePath>
</_FrameworkStaticWebAssetCandidate>
</ItemGroup>
...
</Target>Under normal circumstances, the SDK resolves this package implicitly via targeting packs (KnownAspNetCorePack). Why did that fail here?
How the .NET SDK Decides to Include Microsoft.AspNetCore.App.Internal.Assets
Because this package is internal, the SDK pulls it in conditionally to avoid bloating the manifests of plain Web APIs or Worker services. Microsoft.NET.Sdk.Web.ProjectSystem.targets contains the following check:
<Target Name="ResolveRequiredWebAssets" BeforeTargets="ProcessFrameworkReferences">
<PropertyGroup>
<RequiresAspNetWebAssets Condition="'$(RequiresAspNetWebAssets)' == '' and @(Content->AnyHaveMetadataValue('Extension', '.razor'))>true</RequiresAspNetWebAssets>
</PropertyGroup>
</Target>If the project contains at least one file with a .razor extension, RequiresAspNetWebAssets is set to true, and Microsoft.AspNetCore.App.Internal.Assets is included implicitly.
What Went Wrong?
Shortly before running into this, I refactored the solution: I moved the entire UI layer into a separate Razor Class Library (MyWebApp.Frontend), keeping the main MyWebApp project purely as a host (API, infrastructure, Program.cs). Because testing that specific path slipped through after multiple changes, the bug surfaced later.
Here is the chain of events:
MyWebApp.csprojno longer had any local.razorfiles.- The
ResolveRequiredWebAssetstarget scanned the host project’s files and evaluated the condition tofalse. The SDK does not inspectProjectReferencedependencies transitively when evaluating this property—it only checks the immediate project. RequiresAspNetWebAssetsremainedfalse.ProcessFrameworkReferencesassumed Blazor assets were not required and excludedMicrosoft.AspNetCore.App.Internal.Assetsfrom the restore graph.- The package was omitted from
project.assets.json$\to$ its.targetswere never imported $\to$_AddBlazorFrameworkStaticWebAssetsnever executed $\to$ the scripts never made it into the manifest $\to$ 404 Not Found in the browser.
Solutions
1. Explicitly set the MSBuild property
In MyWebApp.csproj, force-enable the property that the SDK failed to infer automatically:
<PropertyGroup>
<RequiresAspNetWebAssets>true</RequiresAspNetWebAssets>
</PropertyGroup>2. Add an explicit package reference
Alternatively, reference the internal package directly:
<PackageReference
Include="Microsoft.AspNetCore.App.Internal.Assets"
Version="10.0.7"
PrivateAssets="all" />This bypasses the SDK’s conditional RequiresAspNetWebAssets check and directly injects the package into the NuGet restore graph (project.assets.json). The MSBuild targets are imported, _AddBlazorFrameworkStaticWebAssets executes, and the framework scripts appear in the manifest as expected.