Is there an existing issue for this?
Describe the bug
Building a Blazor Web App with the canonical .NET Docker layering pattern
COPY <only *.csproj / *.props / global.json / NuGet.config> ./
RUN dotnet restore
COPY . .
RUN dotnet build --no-restore
RUN dotnet publish --no-build
produces an image where the _framework static web assets are missing. The published app returns 404 for /_framework/blazor.web.js and for the fingerprinted URL the page actually references (/_framework/blazor.web.<hash>.js). Everything else (the app itself, app.css) works.
Nothing fails during the build: dotnet restore, dotnet build, dotnet publish and docker build all exit with 0, and the breakage only shows up at runtime in the browser.
Root cause. blazor.web.js / blazor.server.js are no longer embedded in the assembly; they are delivered by the implicit package Microsoft.AspNetCore.App.Internal.Assets (see #65061, and dotnet/sdk#46165 where making it conditional was intentional). That implicit PackageReference is only added when the project already contains Razor items at evaluation time — i.e. at dotnet restore. In the Docker pattern above the restore happens on a tree that has the .csproj but no .razor files, so the package never ends up in project.assets.json. dotnet build --no-restore then keeps that package graph, the static web assets pipeline never sees the framework assets, and the publish output + *.staticwebassets.endpoints.json are produced without _framework routes.
Steps To Reproduce
Minimal repro repository: https://github.com/borgez/blazor-internal-assets-docker-repro — ./verify.sh builds three images and prints the HTTP status codes:
Dockerfile home=200 /_framework/blazor.web.js=404
Dockerfile.fixed home=200 /_framework/blazor.web.js=200
Dockerfile.copy-first home=200 /_framework/blazor.web.js=200
Repro repo (a hand-written equivalent of dotnet new blazor --interactivity Server, ~6 source files — Microsoft.NET.Sdk.Web, AddRazorComponents().AddInteractiveServerComponents(), app.UseStaticFiles(), app.MapStaticAssets(), app.MapRazorComponents<App>().AddInteractiveServerRenderMode(), and <script src="@Assets["_framework/blazor.web.js"]"></script> in App.razor):
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
WORKDIR /app
EXPOSE 8080
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY --parents **/*.csproj ./ # ← restore sees a tree without .razor
RUN dotnet restore "BlazorMin.csproj"
COPY . .
RUN dotnet build "BlazorMin.csproj" --no-restore -c $BUILD_CONFIGURATION
FROM build AS publish
RUN dotnet publish "BlazorMin.csproj" --no-restore --no-build -c Release -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENV ASPNETCORE_URLS=http://+:8080
ENTRYPOINT ["dotnet", "BlazorMin.dll"]
docker build -t repro .
docker run -d -p 8080:8080 repro
curl -o /dev/null -w '%{http_code}\n' http://localhost:8080/ # 200
curl -o /dev/null -w '%{http_code}\n' http://localhost:8080/_framework/blazor.web.js # 404
Changing only the first COPY line fixes it:
COPY --parents **/*.csproj **/*.razor ./
# /_framework/blazor.web.js → 200, fingerprinted /_framework/blazor.web.f7fxoh55ae.js → 200
What exactly drives the implicit reference — restore-time project content, one variable at a time (dotnet restore on each tree, then inspect project.assets.json):
| project tree at restore time |
Microsoft.AspNetCore.App.Internal.Assets in assets file |
T.csproj only |
absent (0 libraries) |
T.csproj + wwwroot/app.css |
absent |
T.csproj + empty wwwroot/ |
absent |
T.csproj + Components/App.razor |
present (10.0.10) |
Expected Behavior
Any one of these would be acceptable:
- The implicit
PackageReference does not depend on source files being present (copying only project files first is the documented Docker caching pattern), or
dotnet build/dotnet publish fail loudly when the assets file no longer matches the package graph the project evaluates to (currently the mismatch is completely silent), or
- At minimum, a warning when a
Microsoft.NET.Sdk.Web project with Razor content publishes without any _framework static web assets.
Additional context
Reproduced on a real application as well (Blazor Web App, 78 .razor files, MapStaticAssets()):
|
restore without sources |
**/*.razor copied before restore |
project.assets.json |
Microsoft.AspNetCore.App.Internal.Assets missing |
…/10.0.10 present |
/app/publish/wwwroot/_framework/ |
missing |
present (6 files, blazor.web.js ~200 KB) |
*.staticwebassets.endpoints.json |
52 723 B, no _framework/blazor.web.js route |
66 766 B, route present |
GET /_framework/blazor.web.js |
404 |
200 |
Environment: mcr.microsoft.com/dotnet/sdk:10.0 = 10.0.302 (Microsoft.AspNetCore.App.Internal.Assets/10.0.10), runtime mcr.microsoft.com/dotnet/aspnet:10.0; also reproduced with local SDK 10.0.201; Docker 29.8 / buildx 0.37; net10.0.
Related: #65061, dotnet/sdk#46165, #64897, #64545, #65291.
Happy to attach a binlog if that helps.
Is there an existing issue for this?
Describe the bug
Building a Blazor Web App with the canonical .NET Docker layering pattern
produces an image where the
_frameworkstatic web assets are missing. The published app returns 404 for/_framework/blazor.web.jsand for the fingerprinted URL the page actually references (/_framework/blazor.web.<hash>.js). Everything else (the app itself,app.css) works.Nothing fails during the build:
dotnet restore,dotnet build,dotnet publishanddocker buildall exit with 0, and the breakage only shows up at runtime in the browser.Root cause.
blazor.web.js/blazor.server.jsare no longer embedded in the assembly; they are delivered by the implicit packageMicrosoft.AspNetCore.App.Internal.Assets(see #65061, and dotnet/sdk#46165 where making it conditional was intentional). That implicitPackageReferenceis only added when the project already contains Razor items at evaluation time — i.e. atdotnet restore. In the Docker pattern above the restore happens on a tree that has the.csprojbut no.razorfiles, so the package never ends up inproject.assets.json.dotnet build --no-restorethen keeps that package graph, the static web assets pipeline never sees the framework assets, and the publish output +*.staticwebassets.endpoints.jsonare produced without_frameworkroutes.Steps To Reproduce
Minimal repro repository: https://github.com/borgez/blazor-internal-assets-docker-repro —
./verify.shbuilds three images and prints the HTTP status codes:Repro repo (a hand-written equivalent of
dotnet new blazor --interactivity Server, ~6 source files —Microsoft.NET.Sdk.Web,AddRazorComponents().AddInteractiveServerComponents(),app.UseStaticFiles(),app.MapStaticAssets(),app.MapRazorComponents<App>().AddInteractiveServerRenderMode(), and<script src="@Assets["_framework/blazor.web.js"]"></script>inApp.razor):Changing only the first
COPYline fixes it:What exactly drives the implicit reference — restore-time project content, one variable at a time (
dotnet restoreon each tree, then inspectproject.assets.json):Microsoft.AspNetCore.App.Internal.Assetsin assets fileT.csprojonlyT.csproj+wwwroot/app.cssT.csproj+ emptywwwroot/T.csproj+Components/App.razor10.0.10)Expected Behavior
Any one of these would be acceptable:
PackageReferencedoes not depend on source files being present (copying only project files first is the documented Docker caching pattern), ordotnet build/dotnet publishfail loudly when the assets file no longer matches the package graph the project evaluates to (currently the mismatch is completely silent), orMicrosoft.NET.Sdk.Webproject with Razor content publishes without any_frameworkstatic web assets.Additional context
Reproduced on a real application as well (Blazor Web App, 78
.razorfiles,MapStaticAssets()):**/*.razorcopied before restoreproject.assets.jsonMicrosoft.AspNetCore.App.Internal.Assetsmissing…/10.0.10present/app/publish/wwwroot/_framework/blazor.web.js~200 KB)*.staticwebassets.endpoints.json_framework/blazor.web.jsrouteGET /_framework/blazor.web.jsEnvironment:
mcr.microsoft.com/dotnet/sdk:10.0= 10.0.302 (Microsoft.AspNetCore.App.Internal.Assets/10.0.10), runtimemcr.microsoft.com/dotnet/aspnet:10.0; also reproduced with local SDK 10.0.201; Docker 29.8 / buildx 0.37;net10.0.Related: #65061, dotnet/sdk#46165, #64897, #64545, #65291.
Happy to attach a binlog if that helps.