Static Files and Asset Delivery

This section documents ASP.NET Core on .NET 10 (LTS), the current release — the minimal hosting model, the middleware pipeline, dependency injection, Minimal APIs, MVC & Razor Pages, Blazor with the current render modes, SignalR and gRPC, EF Core, ASP.NET Core Identity and policy-based authorization, output caching, rate limiting, and Native-AOT-aware building — as described by the official documentation at Microsoft Learn, which is the reference these pages are written and verified against.

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. .NET ships a major release every November and its APIs continue to evolve: the examples here target .NET 10 / C# 14.

This section’s bibliography lists the reference material consulted while preparing these pages.

ASP.NET Core serves files from wwwroot through middleware, and, for build-time-known assets, through a dedicated static web assets pipeline that adds fingerprinting and compression the classic middleware alone does not.

UseStaticFiles vs. MapStaticAssets

app.UseStaticFiles();     // serves wwwroot as-is; no fingerprinting, no build-time manifest
app.MapStaticAssets();    // .NET 10+: serves the build's static web assets with fingerprinting + compression
app.MapRazorComponents<App>()
   .AddInteractiveServerRenderMode()
   .MapStaticAssets();    // components also resolve <link>/<script> URLs through the fingerprinted map

UseStaticFiles is plain middleware: whatever is under wwwroot at runtime is served, unmodified, with a default Cache-Control the app must configure itself. MapStaticAssets is endpoint-routed and backed by a build-time manifest of every static web asset (including ones contributed by referenced projects and RCLs), so it can rewrite each asset’s referenced URL to a fingerprinted one and serve precompressed variants. Prefer MapStaticAssets for anything produced at build time; fall back to UseStaticFiles (optionally pointed at a different physical/IFileProvider location) for files that only exist at runtime — user uploads, generated reports. See Static files in ASP.NET Core.

Static web assets

Any project (including a referenced Razor class library or NuGet package) can contribute static web assets — files under its own wwwroot are automatically merged into the consuming app’s static asset set at build time, namespaced under _content/{LibraryName}/…​ unless the asset pipeline rewrites the reference for you via MapStaticAssets. This is what makes a UI component library (see UI Component Libraries) able to ship its own CSS/JS without the consuming app copying files manually.

Fingerprinting and compression

MapStaticAssets renames each asset’s served URL to embed a content hash (e.g. app.a1b2c3d4.css), so a browser can cache it indefinitely — a new deployment produces a new hash, and old cached copies simply stop being referenced instead of needing cache invalidation. It also serves precompressed .br/.gz variants generated at publish time when the client’s Accept-Encoding allows it, rather than compressing on every request.

<link rel="stylesheet" href="~/css/app.css" />   @* rendered as .../app.{hash}.css by MapStaticAssets *@

ImportMap

Blazor and other ES-module-based apps use an import map so JS module specifiers (import "./chart.js") resolve to the actual fingerprinted URLs without every source file needing to know the hash:

<ImportMap />   @* emits a <script type="importmap"> built from the static asset manifest *@

This is generated automatically alongside MapStaticAssets and is what lets collocated component JS modules keep plain, hash-free import paths in source while still benefiting from fingerprinted URLs at runtime.

wwwroot conventions

wwwroot/
  css/site.css
  js/site.js
  lib/                # third-party libraries (LibMan or npm-built output)
  favicon.ico

wwwroot is the default web root; anything outside it is not served by either UseStaticFiles or MapStaticAssets unless explicitly configured with an additional IFileProvider. Bundler-built output (Webpack/Vite/esbuild) is typically configured to emit straight into wwwroot so the .NET build treats it like any other static asset.

Razor class library static assets

An RCL’s own wwwroot participates the same way an app’s does — see Razor class libraries for packaging components together with their CSS/JS/images for reuse across projects.

CDN and cache-control strategy

Strategy Notes

Fingerprinted assets, Cache-Control: max-age=31536000, immutable

The default with MapStaticAssets —  safe because the URL itself changes on any content change.

Non-fingerprinted assets (runtime-generated, user uploads)

Set an explicit, shorter Cache-Control (or no-cache plus an ETag/Last-Modified validator) via StaticFileOptions.OnPrepareResponse.

CDN in front of the app

Point the CDN at fingerprinted asset URLs specifically — they can be cached at the edge indefinitely without any purge/invalidation step on deploy.

app.UseStaticFiles(new StaticFileOptions
{
    OnPrepareResponse = ctx =>
        ctx.Context.Response.Headers.CacheControl = "public,max-age=600"
});

See Static files in ASP.NET Core for MapStaticAssets, fingerprinting, and compression details, and Performance and Caching for response/output caching of dynamic content.