--- title: ASP.NET Core Blazor static files author: guardrex description: Learn how to configure and manage static files for Blazor apps. monikerRange: '>= aspnetcore-3.1' ms.author: riande ms.custom: mvc ms.date: 11/12/2024 uid: blazor/fundamentals/static-files --- # ASP.NET Core Blazor static files [!INCLUDE[](~/includes/not-latest-version.md)] This article describes Blazor app configuration for serving static files. ## Static asset delivery in server-side Blazor apps :::moniker range=">= aspnetcore-9.0" Serving static assets is managed by either routing endpoint conventions or a middleware described in the following table. Feature | API | .NET Version | Description --- | --- | :---: | --- Map Static Assets routing endpoint conventions | | .NET 9 or later | Optimizes the delivery of static assets to clients. Static Files Middleware | | All .NET versions | Serves static assets to clients without the optimizations of Map Static Assets but useful for some tasks that Map Static Assets isn't capable of managing. Configure Map Static Assets by calling in the app's request processing pipeline, which performs the following: * Sets the [ETag](https://developer.mozilla.org/docs/Web/HTTP/Headers/ETag) and [Last-Modified](https://developer.mozilla.org/docs/Web/HTTP/Headers/Last-Modified) headers. * Sets [caching headers](https://developer.mozilla.org/docs/Web/HTTP/Headers/Cache-Control). * Uses [Caching Middleware](xref:performance/caching/middleware). * When possible, serves [compressed](xref:performance/response-compression) static assets. * Works with a [Content Delivery Network (CDN)](https://developer.mozilla.org/docs/Glossary/CDN) (for example, [Azure CDN](https://azure.microsoft.com/services/cdn/)) to serve the app's static assets closer to the user. * [Fingerprinting assets](https://developer.mozilla.org/docs/Glossary/Fingerprinting) to prevent reusing old versions of files. Map Static Assets operates by combining build and publish processes to collect information about the static assets in the app. This information is utilized by the runtime library to efficiently serve the static assets to browsers. Map Static Assets can replace in most situations. However, Map Static Assets is optimized for serving the assets from known locations in the app at build and publish time. If the app serves assets from other locations, such as disk or embedded resources, should be used. Map Static Assets () replaces calling in apps that serve Blazor WebAssembly framework files, and explicitly calling in a Blazor Web App isn't necessary because the API is automatically called when invoking . Map Static Assets provides the following benefits that aren't available when calling : * Build-time compression for all the assets in the app, including JavaScript (JS) and stylesheets but excluding image and font assets that are already compressed. [Gzip](https://tools.ietf.org/html/rfc1952) (`Content-Encoding: gz`) compression is used during development. Gzip with [Brotli](https://tools.ietf.org/html/rfc7932) (`Content-Encoding: br`) compression is used during publish. * [Fingerprinting](https://developer.mozilla.org/docs/Glossary/Fingerprinting) for all assets at build time with a [Base64](https://developer.mozilla.org/docs/Glossary/Base64)-encoded string of the [SHA-256](xref:System.Security.Cryptography.SHA256) hash of each file's content. This prevents reusing an old version of a file, even if the old file is cached. Fingerprinted assets are cached using the [`immutable` directive](https://developer.mozilla.org/docs/Web/HTTP/Headers/Cache-Control#directives), which results in the browser never requesting the asset again until it changes. For browsers that don't support the `immutable` directive, a [`max-age` directive](https://developer.mozilla.org/docs/Web/HTTP/Headers/Cache-Control#directives) is added. * Even if an asset isn't fingerprinted, content based `ETags` are generated for each static asset using the fingerprint hash of the file as the `ETag` value. This ensures that the browser only downloads a file if its content changes (or the file is being downloaded for the first time). * Internally, Blazor maps physical assets to their fingerprints, which allows the app to: * Find automatically-generated Blazor assets, such as Razor component scoped CSS for Blazor's [CSS isolation feature](xref:blazor/components/css-isolation), and JS assets described by [JS import maps](https://developer.mozilla.org/docs/Web/HTML/Element/script/type/importmap). * Generate link tags in the `` content of the page to preload assets. * During [Visual Studio Hot Reload](/visualstudio/debugger/hot-reload) development testing: * Integrity information is removed from the assets to avoid issues when a file is changed while the app is running. * Static assets aren't cached to ensure that the browser always retrieves current content. When [Interactive WebAssembly or Interactive Auto render modes](xref:blazor/fundamentals/index#render-modes) are enabled: * Blazor creates an endpoint to expose the resource collection as a JS module. * The URL is emitted to the body of the request as persisted component state when a WebAssembly component is rendered into the page. * During WebAssembly boot, Blazor retrieves the URL, imports the module, and calls a function to retrieve the asset collection and reconstruct it in memory. The URL is specific to the content and cached forever, so this overhead cost is only paid once per user until the app is updated. * The resource collection is also exposed at a human-readable URL (`_framework/resource-collection.js`), so JS has access to the resource collection for [enhanced navigation](xref:blazor/fundamentals/routing#enhanced-navigation-and-form-handling) or to implement features of other frameworks and third-party components. Map Static Assets doesn't provide features for minification or other file transformations. Minification is usually handled by custom code or [third-party tooling](xref:blazor/fundamentals/index#community-links-to-blazor-resources). Static File Middleware () is useful in the following situations that Map Static Assets () can't handle: * Applying a path prefix to Blazor WebAssembly static asset files, which is covered in the [Prefix for Blazor WebAssembly assets](#prefix-for-blazor-webassembly-assets) section. * Configuring file mappings of extensions to specific content types and setting static file options, which is covered in the [File mappings and static file options](#file-mappings-and-static-file-options) section. For more information, see . ## Deliver assets with Map Static Assets routing endpoint conventions *This section applies to server-side Blazor apps.* Assets are delivered via the property, which resolves the fingerprinted URL for a given asset. In the following example, Bootstrap, the Blazor project template app stylesheet (`app.css`), and the [CSS isolation stylesheet](xref:blazor/components/css-isolation) (based on an app's namespace of `BlazorSample`) are linked in a root component, typically the `App` component (`Components/App.razor`): ```razor ``` ## Import maps *This section applies to server-side Blazor apps.* The Import Map component () represents an import map element (``) that defines the import map for module scripts. The Import Map component is placed in `` content of the root component, typically the `App` component (`Components/App.razor`). ```razor ``` If a custom isn't assigned to an Import Map component, the import map is generated based on the app's assets. The following examples demonstrate custom import map definitions and the import maps that they create. Basic import map: ```csharp new ImportMapDefinition( new Dictionary { { "jquery", "https://cdn.example.com/jquery.js" }, }, null, null); ``` The preceding code results in the following import map: ```json { "imports": { "jquery": "https://cdn.example.com/jquery.js" } } ``` Scoped import map: ```csharp new ImportMapDefinition( null, new Dictionary> { ["/scoped/"] = new Dictionary { { "jquery", "https://cdn.example.com/jquery.js" }, } }, null); ``` The preceding code results in the following import map: ```json { "scopes": { "/scoped/": { "jquery": "https://cdn.example.com/jquery.js" } } } ``` Import map with integrity: ```csharp new ImportMapDefinition( new Dictionary { { "jquery", "https://cdn.example.com/jquery.js" }, }, null, new Dictionary { { "https://cdn.example.com/jquery.js", "sha384-abc123" }, }); ``` The preceding code results in the following import map: ```json { "imports": { "jquery": "https://cdn.example.com/jquery.js" }, "integrity": { "https://cdn.example.com/jquery.js": "sha384-abc123" } } ``` Combine import map definitions () with . Import map created from a that maps static assets to their corresponding unique URLs: ```csharp ImportMapDefinition.FromResourceCollection( new ResourceAssetCollection( [ new ResourceAsset( "jquery.fingerprint.js", [ new ResourceAssetProperty("integrity", "sha384-abc123"), new ResourceAssetProperty("label", "jquery.js"), ]) ])); ``` The preceding code results in the following import map: ```json { "imports": { "./jquery.js": "./jquery.fingerprint.js" }, "integrity": { "jquery.fingerprint.js": "sha384-abc123" } } ``` :::moniker-end :::moniker range="< aspnetcore-9.0" Configure Static File Middleware to serve static assets to clients by calling in the app's request processing pipeline. For more information, see . :::moniker-end In releases prior to .NET 8, Blazor framework static files, such as the Blazor script, are served via Static File Middleware. In .NET 8 or later, Blazor framework static files are mapped using endpoint routing, and Static File Middleware is no longer used. ## Summary of static file `` `href` formats *This section applies to all .NET releases and Blazor apps.* The following tables summarize static file `` `href` formats by .NET release. :::moniker range=">= aspnetcore-6.0" For the location of `` content where static file links are placed, see . Static asset links can also be supplied using [`` components](xref:blazor/components/control-head-content) in individual Razor components. :::moniker-end :::moniker range="< aspnetcore-6.0" For the location of `` content where static file links are placed, see . :::moniker-end ### .NET 9 or later App type | `href` value | Examples --- | --- | --- Blazor Web App | `@Assets["{PATH}"]` | ``
`` Blazor Server† | `@Assets["{PATH}"]` | ``
`` Standalone Blazor WebAssembly | `{PATH}` | ``
`` ### .NET 8.x App type | `href` value | Examples --- | --- | --- Blazor Web App | `{PATH}` | ``
`` Blazor Server† | `{PATH}` | ``
`` Standalone Blazor WebAssembly | `{PATH}` | ``
`` ### .NET 7.x or earlier App type | `href` value | Examples --- | --- | --- Blazor Server† | `{PATH}` | ``
`` Hosted Blazor WebAssembly‡ | `{PATH}` | ``
`` Blazor WebAssembly | `{PATH}` | ``
`` †Blazor Server is supported in .NET 8 or later but is no longer a project template after .NET 7. ‡We recommend updating Hosted Blazor WebAssembly apps to Blazor Web Apps when adopting .NET 8 or later. :::moniker range=">= aspnetcore-8.0" ## Static Web Asset Project Mode *This section applies to the `.Client` project of a Blazor Web App.* The required `Default` setting in the `.Client` project of a Blazor Web App reverts Blazor WebAssembly static asset behaviors back to the defaults, so that the project behaves as part of the hosted project. The Blazor WebAssembly SDK (`Microsoft.NET.Sdk.BlazorWebAssembly`) configures static web assets in a specific way to work in "standalone" mode with a server simply consuming the outputs from the library. This isn't appropriate for a Blazor Web App, where the WebAssembly portion of the app is a logical part of the host and must behave more like a library. For example, the project doesn't expose the styles bundle (for example, `BlazorSample.Client.styles.css`) and instead only provides the host with the project bundle, so that the host can include it in its own styles bundle. Changing the value (`Default`) of `` or removing the property from the `.Client` project isn't supported. :::moniker-end ## Static files in non-`Development` environments *This section applies to server-side static files.* When running an app locally, static web assets are only enabled in the environment. To enable static files for environments other than during local development and testing (for example, ), call on the in the `Program` file. > [!WARNING] > Call for the ***exact environment*** to prevent activating the feature in production, as it serves files from separate locations on disk *other than from the project* if called in a production environment. The example in this section checks for the environment by calling . ```csharp if (builder.Environment.IsStaging()) { builder.WebHost.UseStaticWebAssets(); } ``` :::moniker range=">= aspnetcore-8.0" ## Prefix for Blazor WebAssembly assets *This section applies to Blazor Web Apps.* Use the endpoint option to set the path string that indicates the prefix for Blazor WebAssembly assets. The path must correspond to a referenced Blazor WebAssembly application project. ```csharp endpoints.MapRazorComponents() .AddInteractiveWebAssemblyRenderMode(options => options.PathPrefix = "{PATH PREFIX}"); ``` In the preceding example, the `{PATH PREFIX}` placeholder is the path prefix and must start with a forward slash (`/`). In the following example, the path prefix is set to `/path-prefix`: ```csharp endpoints.MapRazorComponents() .AddInteractiveWebAssemblyRenderMode(options => options.PathPrefix = "/path-prefix"); ``` :::moniker-end ## Static web asset base path :::moniker range=">= aspnetcore-8.0" *This section applies to standalone Blazor WebAssembly apps.* Publishing the app places the app's static assets, including Blazor framework files (`_framework` folder assets), at the root path (`/`) in published output. The `` property specified in the project file (`.csproj`) sets the base path to a non-root path: ```xml {PATH} ``` In the preceding example, the `{PATH}` placeholder is the path. Without setting the `` property, a standalone app is published at `/BlazorStandaloneSample/bin/Release/{TFM}/publish/wwwroot/`. In the preceding example, the `{TFM}` placeholder is the [Target Framework Moniker (TFM)](/dotnet/standard/frameworks) (for example, `net6.0`). If the `` property in a standalone Blazor WebAssembly app sets the published static asset path to `app1`, the root path to the app in published output is `/app1`. In the standalone Blazor WebAssembly app's project file (`.csproj`): ```xml app1 ``` In published output, the path to the standalone Blazor WebAssembly app is `/BlazorStandaloneSample/bin/Release/{TFM}/publish/wwwroot/app1/`. In the preceding example, the `{TFM}` placeholder is the [Target Framework Moniker (TFM)](/dotnet/standard/frameworks) (for example, `net6.0`). :::moniker-end :::moniker range="< aspnetcore-8.0" *This section applies to standalone Blazor WebAssembly apps and hosted Blazor WebAssembly solutions.* Publishing the app places the app's static assets, including Blazor framework files (`_framework` folder assets), at the root path (`/`) in published output. The `` property specified in the project file (`.csproj`) sets the base path to a non-root path: ```xml {PATH} ``` In the preceding example, the `{PATH}` placeholder is the path. Without setting the `` property, the client app of a hosted solution or a standalone app is published at the following paths: * In the **:::no-loc text="Server":::** project of a hosted Blazor WebAssembly solution: `/BlazorHostedSample/Server/bin/Release/{TFM}/publish/wwwroot/` * In a standalone Blazor WebAssembly app: `/BlazorStandaloneSample/bin/Release/{TFM}/publish/wwwroot/` If the `` property in the **:::no-loc text="Client":::** project of a hosted Blazor WebAssembly app or in a standalone Blazor WebAssembly app sets the published static asset path to `app1`, the root path to the app in published output is `/app1`. In the **:::no-loc text="Client":::** app's project file (`.csproj`) or the standalone Blazor WebAssembly app's project file (`.csproj`): ```xml app1 ``` In published output: * Path to the client app in the **:::no-loc text="Server":::** project of a hosted Blazor WebAssembly solution: `/BlazorHostedSample/Server/bin/Release/{TFM}/publish/wwwroot/app1/` * Path to a standalone Blazor WebAssembly app: `/BlazorStandaloneSample/bin/Release/{TFM}/publish/wwwroot/app1/` The `` property is most commonly used to control the paths to published static assets of multiple Blazor WebAssembly apps in a single hosted deployment. For more information, see . The property is also effective in standalone Blazor WebAssembly apps. In the preceding examples, the `{TFM}` placeholder is the [Target Framework Moniker (TFM)](/dotnet/standard/frameworks) (for example, `net6.0`). :::moniker-end ## File mappings and static file options *This section applies to server-side static files.* :::moniker range=">= aspnetcore-8.0" To create additional file mappings with a or configure other , use **one** of the following approaches. In the following examples, the `{EXTENSION}` placeholder is the file extension, and the `{CONTENT TYPE}` placeholder is the content type. The namespace for the following API is . * Configure options through [dependency injection (DI)](xref:blazor/fundamentals/dependency-injection) in the `Program` file using : ```csharp var provider = new FileExtensionContentTypeProvider(); provider.Mappings["{EXTENSION}"] = "{CONTENT TYPE}"; builder.Services.Configure(options => { options.ContentTypeProvider = provider; }); app.UseStaticFiles(); ``` * Pass the directly to in the `Program` file: ```csharp var provider = new FileExtensionContentTypeProvider(); provider.Mappings["{EXTENSION}"] = "{CONTENT TYPE}"; app.UseStaticFiles(new StaticFileOptions { ContentTypeProvider = provider }); ``` :::moniker-end :::moniker range="< aspnetcore-8.0" To create additional file mappings with a or configure other , use **one** of the following approaches. In the following examples, the `{EXTENSION}` placeholder is the file extension, and the `{CONTENT TYPE}` placeholder is the content type. * Configure options through [dependency injection (DI)](xref:blazor/fundamentals/dependency-injection) in the `Program` file using : ```csharp using Microsoft.AspNetCore.StaticFiles; ... var provider = new FileExtensionContentTypeProvider(); provider.Mappings["{EXTENSION}"] = "{CONTENT TYPE}"; builder.Services.Configure(options => { options.ContentTypeProvider = provider; }); ``` This approach configures the same file provider used to serve the Blazor script. Make sure that your custom configuration doesn't interfere with serving the Blazor script. For example, don't remove the mapping for JavaScript files by configuring the provider with `provider.Mappings.Remove(".js")`. * Use two calls to in the `Program` file: * Configure the custom file provider in the first call with . * The second middleware serves the Blazor script, which uses the default static files configuration provided by the Blazor framework. ```csharp using Microsoft.AspNetCore.StaticFiles; ... var provider = new FileExtensionContentTypeProvider(); provider.Mappings["{EXTENSION}"] = "{CONTENT TYPE}"; app.UseStaticFiles(new StaticFileOptions { ContentTypeProvider = provider }); app.UseStaticFiles(); ``` * You can avoid interfering with serving `_framework/blazor.server.js` by using to execute a custom Static File Middleware: ```csharp app.MapWhen(ctx => !ctx.Request.Path .StartsWithSegments("/_framework/blazor.server.js"), subApp => subApp.UseStaticFiles(new StaticFileOptions() { ... })); ``` :::moniker-end :::moniker range=">= aspnetcore-8.0" ## Serve files from multiple locations *The guidance in this section only applies to Blazor Web Apps.* To serve files from multiple locations with a : * Add the namespace for to the top of the `Program` file of the server project. * In the server project's `Program` file ***before*** the call to : * Create a with the path to the static assets. * Create a from the and the . Assign the composite file provider back to the app's . Example: Create a new folder in the server project named `AdditionalStaticAssets`. Place an image into the folder. Add the following `using` statement to the top of the server project's `Program` file: ```csharp using Microsoft.Extensions.FileProviders; ``` In the server project's `Program` file ***before*** the call to , add the following code: ```csharp var secondaryProvider = new PhysicalFileProvider( Path.Combine(builder.Environment.ContentRootPath, "AdditionalStaticAssets")); app.Environment.WebRootFileProvider = new CompositeFileProvider( app.Environment.WebRootFileProvider, secondaryProvider); ``` In the app's `Home` component (`Home.razor`) markup, reference the image with an `` tag: ```razor {ALT TEXT} ``` In the preceding example: * The `{IMAGE FILE NAME}` placeholder is the image file name. There's no need to provide a path segment if the image file is at the root of the `AdditionalStaticAssets` folder. * The `{ALT TEXT}` placeholder is the image alternate text. Run the app. :::moniker-end ## Additional resources :::moniker range=">= aspnetcore-8.0" * [App base path](xref:blazor/host-and-deploy/index#app-base-path) * [Avoid file capture in a route parameter](xref:blazor/fundamentals/routing#avoid-file-capture-in-a-route-parameter) :::moniker-end :::moniker range="< aspnetcore-8.0" * [App base path](xref:blazor/host-and-deploy/index#app-base-path) * [Avoid file capture in a route parameter](xref:blazor/fundamentals/routing#avoid-file-capture-in-a-route-parameter) * :::moniker-end