Repository: dotnet/AspNetCore.Docs
Pull request #35057: [Pre3] Blazor WASM fingerprinting and environment property
Author: login: guardrex
Created: 2025-03-26T15:25:22Z | Merged: 2025-04-08T11:23:24Z

Fixes #35023
Addresses #34948
Addresses #34437
Fixes #35075

Covering two Blazor WASM features now on this PR ...

* Fingerprinting
* `<WasmApplicationEnvironmentName>` to set the environment

<!-- PREVIEW-TABLE-START -->

---

#### Internal previews

| 📄 File | 🔗 Preview link |
|:--|:--|
| [aspnetcore/blazor/components/built-in-components.md](https://github.com/dotnet/AspNetCore.Docs/blob/517a64adb10bc8bc2435bbc8f0347fea10c7cbc2/aspnetcore/blazor/components/built-in-components.md) | [aspnetcore/blazor/components/built-in-components](https://review.learn.microsoft.com/en-us/aspnet/core/blazor/components/built-in-components?branch=pr-en-us-35057) |
| [aspnetcore/blazor/fundamentals/environments.md](https://github.com/dotnet/AspNetCore.Docs/blob/517a64adb10bc8bc2435bbc8f0347fea10c7cbc2/aspnetcore/blazor/fundamentals/environments.md) | [aspnetcore/blazor/fundamentals/environments](https://review.learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/environments?branch=pr-en-us-35057) |
| [aspnetcore/blazor/fundamentals/static-files.md](https://github.com/dotnet/AspNetCore.Docs/blob/517a64adb10bc8bc2435bbc8f0347fea10c7cbc2/aspnetcore/blazor/fundamentals/static-files.md) | [aspnetcore/blazor/fundamentals/static-files](https://review.learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/static-files?branch=pr-en-us-35057) |
| [aspnetcore/fundamentals/map-static-files.md](https://github.com/dotnet/AspNetCore.Docs/blob/517a64adb10bc8bc2435bbc8f0347fea10c7cbc2/aspnetcore/fundamentals/map-static-files.md) | [aspnetcore/fundamentals/map-static-files](https://review.learn.microsoft.com/en-us/aspnet/core/fundamentals/map-static-files?branch=pr-en-us-35057) |


<!-- PREVIEW-TABLE-END -->

Linked issue #35023: [Pre3] Fingerprinting in Blazor WebAssembly
### Description

* https://github.com/dotnet/AspNetCore.Docs/issues/34948#issuecomment-2735891258
* https://github.com/dotnet/aspnetcore/issues/60536


### Page URL

https://learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/static-files?view=aspnetcore-9.0

### Content source URL

https://github.com/dotnet/AspNetCore.Docs/blob/main/aspnetcore/blazor/fundamentals/static-files.md

### Document ID

25458428-030f-6acb-457e-1b4dfa722043

### Platform Id

88af5647-85fa-eaf4-cbe1-4d0d4fdf521b

### Article author

@guardrex

### Metadata

* ID: b0af7f84-f8dd-92d9-dacd-2aab76d3a039
* PlatformId: 88af5647-85fa-eaf4-cbe1-4d0d4fdf521b 
* Service: **aspnet-core**
* Sub-service: **blazor**

[Related Issues](https://github.com/dotnet/AspNetCore.Docs/issues?q=is%3Aissue+is%3Aopen+25458428-030f-6acb-457e-1b4dfa722043)
Linked issue comments: user: login: github-actions[bot]
created_at: 2025-03-21T17:21:21Z
body: ### 🌱🐇 **_Spring is coming. Hang in there! I'm pull'in for ya!_** 🐣🌻

*Stand-by!* ... A green dinosaur 🦖 will be along shortly to assist.
author_association: CONTRIBUTOR

Diff:
diff --git a/aspnetcore/blazor/components/built-in-components.md b/aspnetcore/blazor/components/built-in-components.md
index 2bd4b5fcdf9c..05470eb88d2e 100644
--- a/aspnetcore/blazor/components/built-in-components.md
+++ b/aspnetcore/blazor/components/built-in-components.md
@@ -27,7 +27,7 @@ The following built-in Razor components are provided by the Blazor framework. Fo
 * [`FocusOnNavigate`](xref:blazor/fundamentals/routing#focus-an-element-on-navigation)
 * [`HeadContent`](xref:blazor/components/control-head-content)
 * [`HeadOutlet`](xref:blazor/components/control-head-content)
-* [`ImportMap`](xref:blazor/fundamentals/static-files#import-maps)
+* [`ImportMap`](xref:blazor/fundamentals/static-files#importmap-component)
 * [`InputCheckbox`](xref:blazor/forms/input-components)
 * [`InputDate`](xref:blazor/forms/input-components)
 * [`InputFile`](xref:blazor/file-uploads)
diff --git a/aspnetcore/blazor/fundamentals/environments.md b/aspnetcore/blazor/fundamentals/environments.md
index eb33daa9f157..c79dc4f34594 100644
--- a/aspnetcore/blazor/fundamentals/environments.md
+++ b/aspnetcore/blazor/fundamentals/environments.md
@@ -28,34 +28,58 @@ We recommend the following conventions:
 
 ## Set the environment
 
-:::moniker range=">= aspnetcore-8.0"
-
 The environment is set using any of the following approaches:
 
-* Blazor Web App: Use any of the approaches described in <xref:fundamentals/environments> for general ASP.NET Core apps.
-* Blazor Web App or standalone Blazor WebAssembly: [Blazor start configuration](#set-the-client-side-environment-via-blazor-startup-configuration)
-* Standalone Blazor WebAssembly: [`Blazor-Environment` header](#set-the-client-side-environment-via-header)
-* Blazor Web App or standalone Blazor WebAssembly: [Azure App Service](#set-the-environment-for-azure-app-service)
+:::moniker range=">= aspnetcore-10.0"
+
+* Blazor Web App or Blazor Server: Use any of the approaches described in <xref:fundamentals/environments> for general ASP.NET Core apps.
+* Any Blazor app: [Blazor start configuration](#set-the-client-side-environment-via-blazor-startup-configuration)
+* Standalone Blazor WebAssembly: `<WasmApplicationEnvironmentName>` property
+
+On the client for a Blazor Web App, the environment is determined from the server via an HTML comment that developers don't interact with:
+
+```html
+<!--Blazor-WebAssembly:{"environmentName":"Development", ...}-->
+```
+
+For a standalone Blazor WebAssembly app, set the environment with the `<WasmApplicationEnvironmentName>` property in the app's project file (`.csproj`). The following example sets the `Staging` environment:
+
+```xml
+<WasmApplicationEnvironmentName>Staging</WasmApplicationEnvironmentName>
+```
+
+The default environments are `Development` for build and `Production` for publish.
+
+:::moniker-end
+
+:::moniker range=">= aspnetcore-8.0 < aspnetcore-10.0"
+
+* Blazor Web App or Blazor Server: Use any of the approaches described in <xref:fundamentals/environments> for general ASP.NET Core apps.
+* Any Blazor app:
+  * [Blazor start configuration](#set-the-client-side-environment-via-blazor-startup-configuration)
+  * [Azure App Service](#set-the-environment-for-azure-app-service)
+* Blazor WebAssembly: [`Blazor-Environment` header](#set-the-client-side-environment-via-header)
 
 On the client for a Blazor Web App, the environment is determined from the server via a middleware that communicates the environment to the browser via a header named `Blazor-Environment`. The header sets the environment when the <xref:Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHost> is created in the client-side `Program` file (<xref:Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder.CreateDefault%2A?displayProperty=nameWithType>).
 
+For a standalone Blazor WebAssembly app running locally, the development server adds the `Blazor-Environment` header with the environment name obtained from the hosting environment. The hosting environment sets the environment from the `ASPNETCORE_ENVIRONMENT` environment variable established by the project's `Properties/launchSettings.json` file. The default value of the environment variable in a project created from the Blazor WebAssembly project template is `Development`. For more information, see the [Set the client-side environment via header](#set-the-client-side-environment-via-header) section.
+
 :::moniker-end
 
 :::moniker range="< aspnetcore-8.0"
 
-The environment is set using any of the following approaches:
-
 * Blazor Server: Use any of the approaches described in <xref:fundamentals/environments> for general ASP.NET Core apps.
-* Blazor Server or Blazor WebAssembly: [Blazor start configuration](#set-the-client-side-environment-via-blazor-startup-configuration)
+* Blazor Server or Blazor WebAssembly:
+  * [Blazor start configuration](#set-the-client-side-environment-via-blazor-startup-configuration)
+  * [Azure App Service](#set-the-environment-for-azure-app-service)
 * Blazor WebAssembly: [`Blazor-Environment` header](#set-the-client-side-environment-via-header)
-* Blazor Server or Blazor WebAssembly: [Azure App Service](#set-the-environment-for-azure-app-service)
-
-On the client for a Blazor Web App or the client of a hosted Blazor WebAssembly app, the environment is determined from the server via a middleware that communicates the environment to the browser via a header named `Blazor-Environment`. The header sets the environment when the <xref:Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHost> is created in the client-side `Program` file (<xref:Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder.CreateDefault%2A?displayProperty=nameWithType>).
 
-:::moniker-end
+On the client of a hosted Blazor WebAssembly app, the environment is determined from the server via a middleware that communicates the environment to the browser via a header named `Blazor-Environment`. The header sets the environment when the <xref:Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHost> is created in the client-side `Program` file (<xref:Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder.CreateDefault%2A?displayProperty=nameWithType>).
 
 For a standalone Blazor WebAssembly app running locally, the development server adds the `Blazor-Environment` header with the environment name obtained from the hosting environment. The hosting environment sets the environment from the `ASPNETCORE_ENVIRONMENT` environment variable established by the project's `Properties/launchSettings.json` file. The default value of the environment variable in a project created from the Blazor WebAssembly project template is `Development`. For more information, see the [Set the client-side environment via header](#set-the-client-side-environment-via-header) section.
 
+:::moniker-end
+
 For app's running locally in development, the app defaults to the `Development` environment. Publishing the app defaults the environment to `Production`.
 
 :::moniker range="< aspnetcore-5.0"
@@ -111,10 +135,14 @@ Standalone Blazor WebAssembly:
 
 **In the preceding example, the `{BLAZOR SCRIPT}` placeholder is the Blazor script path and file name.** For the location of the script, see <xref:blazor/project-structure#location-of-the-blazor-script>.
 
+:::moniker range="< aspnetcore-10.0"
+
 Using the `environment` property overrides the environment set by the [`Blazor-Environment` header](#set-the-client-side-environment-via-header).
 
 The preceding approach sets the client's environment without changing the `Blazor-Environment` header's value, nor does it change the server project's console logging of the startup environment for a Blazor Web App that has adopted global Interactive WebAssembly rendering.
 
+:::moniker-end
+
 To log the environment to the console in either a standalone Blazor WebAssembly app or the `.Client` project of a Blazor Web App, place the following C# code in the `Program` file after the <xref:Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHost> is created with <xref:Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder.CreateDefault%2A?displayProperty=nameWithType> and before the line that builds and runs the project (`await builder.Build().RunAsync();`):
 
 ```csharp
@@ -124,6 +152,8 @@ Console.WriteLine(
 
 For more information on Blazor startup, see <xref:blazor/fundamentals/startup>.
 
+:::moniker range="< aspnetcore-10.0"
+
 ## Set the client-side environment via header
 
 Blazor WebAssembly apps can set the environment with the `Blazor-Environment` header. Specifically, the response header must be set on the `_framework/blazor.boot.json` file, but there's no harm setting the header on file server responses for other Blazor file requests or the entire Blazor deployment.
@@ -191,7 +221,7 @@ For more information:
 * [Apache documentation (search the latest release for "`mod_headers`")](https://httpd.apache.org/docs/)
 * [Blazor WebAssembly Apache hosting guidance](xref:blazor/host-and-deploy/webassembly#apache)
 
-## Set the environment for Azure App Service
+### Set the environment for Azure App Service
 
 <!-- UPDATE 10.0 The underlying problem with app settings filename 
                  case sensitivity is tracked for 10.0 by ...
@@ -213,6 +243,8 @@ When the app is loaded in the browser, the response header collection for `blazo
 
 App settings from the `appsettings.{ENVIRONMENT}.json` file are loaded by the app, where the `{ENVIRONMENT}` placeholder is the app's environment. In the preceding example, settings from the `appsettings.Staging.json` file are loaded.
 
+:::moniker-end
+
 ## Read the environment in a Blazor WebAssembly app
 
 Obtain the app's environment in a component by injecting <xref:Microsoft.AspNetCore.Components.WebAssembly.Hosting.IWebAssemblyHostEnvironment> and reading the <xref:Microsoft.AspNetCore.Components.WebAssembly.Hosting.IWebAssemblyHostEnvironment.Environment> property.
diff --git a/aspnetcore/blazor/fundamentals/static-files.md b/aspnetcore/blazor/fundamentals/static-files.md
index 47efedc64f43..8ce0029fca6b 100644
--- a/aspnetcore/blazor/fundamentals/static-files.md
+++ b/aspnetcore/blazor/fundamentals/static-files.md
@@ -64,11 +64,11 @@ Assets are delivered via the <xref:Microsoft.AspNetCore.Components.ComponentBase
 <link rel="stylesheet" href="@Assets["BlazorSample.styles.css"]" />
 ```
 
-## Import maps
+## `ImportMap` component
 
 *This section applies to server-side Blazor apps.*
 
-The Import Map component (<xref:Microsoft.AspNetCore.Components.ImportMap>) represents an import map element (`<script type="importmap"></script>`) that defines the import map for module scripts. The Import Map component is placed in `<head>` content of the root component, typically the `App` component (`Components/App.razor`).
+The `ImportMap` component (<xref:Microsoft.AspNetCore.Components.ImportMap>) represents an import map element (`<script type="importmap"></script>`) that defines the import map for module scripts. The Import Map component is placed in `<head>` content of the root component, typically the `App` component (`Components/App.razor`).
 
 ```razor
 <ImportMap />
@@ -76,6 +76,9 @@ The Import Map component (<xref:Microsoft.AspNetCore.Components.ImportMap>) repr
 
 If a custom <xref:Microsoft.AspNetCore.Components.ImportMapDefinition> isn't assigned to an Import Map component, the import map is generated based on the app's assets.
 
+> [!NOTE]
+> <xref:Microsoft.AspNetCore.Components.ImportMapDefinition> instances are expensive to create, so we recommended caching them when creating an additional instance.
+
 The following examples demonstrate custom import map definitions and the import maps that they create.
 
 Basic import map:
@@ -197,9 +200,60 @@ For examples of how to address the policy violation with Subresource Integrity (
 
 Configure Static File Middleware to serve static assets to clients by calling <xref:Microsoft.AspNetCore.Builder.StaticFileExtensions.UseStaticFiles%2A> in the app's request processing pipeline. For more information, see <xref:fundamentals/static-files>.
 
+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.
+
 :::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.
+:::moniker range=">= aspnetcore-10.0"
+
+## Fingerprint client-side static assets in standalone Blazor WebAssembly apps
+
+In standalone Blazor WebAssembly apps during build/publish, the framework overrides placeholders in `index.html` with values computed during build to fingerprint static assets for client-side rendering. A [fingerprint](https://wikipedia.org/wiki/Fingerprint_(computing)) is placed into the `blazor.webassembly.js` script file name, and an import map is generated for other .NET assets.
+
+The following configuration must be present in the `wwwwoot/index.html` file of a standalone Blazor WebAssembly app to adopt fingerprinting:
+
+```html
+<head>
+    ...
+    <script type="importmap"></script>
+    ...
+</head>
+
+<body>
+    ...
+    <script src="_framework/blazor.webassembly#[.{fingerprint}].js"></script>
+    ...
+</body>
+
+</html>
+```
+
+In the project file (`.csproj`), the `<WriteImportMapToHtml>` property is set to `true`:
+
+```xml
+<PropertyGroup>
+  <WriteImportMapToHtml>true</WriteImportMapToHtml>
+</PropertyGroup>
+```
+
+When resolving imports for JavaScript interop, the import map is used by the browser resolve fingerprinted files.
+
+## Fingerprint client-side static assets in Blazor Web Apps
+
+For client-side rendering (CSR) in Blazor Web Apps (Interactive Auto or Interactive WebAssembly render modes), static asset server-side [fingerprinting](https://wikipedia.org/wiki/Fingerprint_(computing)) is enabled by adopting [Map Static Assets routing endpoint conventions (`MapStaticAssets`)](xref:fundamentals/map-static-files), [`ImportMap` component](xref:blazor/fundamentals/static-files#importmap-component), and the <xref:Microsoft.AspNetCore.Components.ComponentBase.Assets?displayProperty=nameWithType> property (`@Assets["..."]`).
+
+To fingerprint additional JavaScript modules for CSR, use the `<StaticWebAssetFingerprintPattern>` item in the app's project file (`.csproj`). In the following example, a fingerprint is added for all developer-supplied `.mjs` files in the app:
+
+```xml
+<ItemGroup>
+  <StaticWebAssetFingerprintPattern Include="JSModule" Pattern="*.mjs" 
+    Expression="#[.{fingerprint}]!" />
+</ItemGroup>
+```
+
+When resolving imports for JavaScript interop, the import map is used by the browser resolve fingerprinted files.
+
+:::moniker-end
 
 ## Summary of static file `<link>` `href` formats
 
diff --git a/aspnetcore/fundamentals/map-static-files.md b/aspnetcore/fundamentals/map-static-files.md
index 103802587c17..402b06c2bd16 100644
--- a/aspnetcore/fundamentals/map-static-files.md
+++ b/aspnetcore/fundamentals/map-static-files.md
@@ -45,7 +45,7 @@ Creating performant web apps requires optimizing asset delivery to the browser.
 * Use [Caching Middleware](xref:performance/caching/middleware).
 * Serve [compressed](/aspnet/core/performance/response-compression) versions of the assets when possible. This optimization doesn't include minification.
 * Use a [CDN](/microsoft-365/enterprise/content-delivery-networks?view=o365-worldwide&preserve-view=true) to serve the assets closer to the user.
-* [Fingerprinting assets](https://developer.mozilla.org/docs/Glossary/Fingerprinting) to prevent reusing old versions of files.
+* [Fingerprinting assets](https://en.wikipedia.org/wiki/Fingerprint_(computing)) to prevent reusing old versions of files.
 
 `MapStaticAssets`:
 
diff --git a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md
index 0c954e38f451..132a63bb47f3 100644
--- a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md
+++ b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md
@@ -132,4 +132,74 @@ Also make this change in the *Routing* article at Line 1633.
 
 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 
+### Client-side fingerprinting
+
+Last year, the release of .NET 9 introduced [server-side fingerprinting](https://en.wikipedia.org/wiki/Fingerprint_(computing)) of static assets in Blazor Web Apps with the introduction of [Map Static Assets routing endpoint conventions (`MapStaticAssets`)](xref:fundamentals/map-static-files), the [`ImportMap` component](xref:blazor/fundamentals/static-files#importmap-component), and the <xref:Microsoft.AspNetCore.Components.ComponentBase.Assets?displayProperty=nameWithType> property (`@Assets["..."]`) to resolve fingerprinted JavaScript modules. For .NET 10, you can opt-into client-side fingerprinting of JavaScript modules for standalone Blazor WebAssembly apps.
+
+In standalone Blazor WebAssembly apps during build/publish, the framework overrides placeholders in `index.html` with values computed during build to fingerprint static assets. A fingerprint is placed into the `blazor.webassembly.js` script file name.
+
+The following changes must be made in the `wwwwoot/index.html` file to adopt the fingerprinting feature. The standalone Blazor WebAssembly project template will be updated to include these changes in an upcoming preview release:
+
+```diff
+<head>
+    ...
++   <script type="importmap"></script>
+</head>
+
+<body>
+    ...
+-   <script src="_framework/blazor.webassembly.js"></script>
++   <script src="_framework/blazor.webassembly#[.{fingerprint}].js"></script>
+</body>
+
+</html>
+```
+
+In the project file (`.csproj`), add the `<WriteImportMapToHtml>` property set to `true`:
+
+```diff
+<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
+
+  <PropertyGroup>
+    <TargetFramework>net10.0</TargetFramework>
+    <Nullable>enable</Nullable>
+    <ImplicitUsings>enable</ImplicitUsings>
++   <WriteImportMapToHtml>true</WriteImportMapToHtml>
+  </PropertyGroup>
+</Project>
+```
+
+To fingerprint additional JS modules in standalone Blazor WebAssembly apps, use the `<StaticWebAssetFingerprintPattern>` property in the app's project file (`.csproj`).
+
+In the following example, a fingerprint is added for all developer-supplied `.mjs` files in the app:
+
+```xml
+<StaticWebAssetFingerprintPattern Include="JSModule" Pattern="*.mjs" 
+  Expression="#[.{fingerprint}]!" />
+```
+
+The files are automatically placed into the import map:
+
+* Automatically for Blazor Web App CSR.
+* When opting-into module fingerprinting in standalone Blazor WebAssembly apps per the preceding instructions.
+
+When resolving the import for JavaScript interop, the import map is used by the browser resolve fingerprinted files.
+
+### Set the environment in standalone Blazor WebAssembly apps
+
+The `Properties/launchSettings.json` file is no longer used to control the environment in standalone Blazor WebAssembly apps.
+
+Starting in .NET 10, set the environment with the `<WasmApplicationEnvironmentName>` property in the app's project file (`.csproj`).
+
+The following example sets the app's environment to `Staging`:
+
+```xml
+<WasmApplicationEnvironmentName>Staging</WasmApplicationEnvironmentName>
+```
+
+The default environments are:
+
+* `Development` for build.
+* `Production` for publish.
+
 -->


Reviews:
user: login: maraf
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-27T14:14:00Z
user: login: guardrex
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-27T14:21:51Z
user: login: maraf
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-27T16:38:13Z
user: login: guardrex
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-27T18:46:34Z
user: login: maraf
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-28T10:34:22Z
user: login: maraf
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-02T15:24:02Z
user: login: guardrex
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-02T15:24:51Z
user: login: guardrex
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-02T15:44:24Z
user: login: danroth27
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-05T02:17:34Z
user: login: guardrex
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-05T02:24:11Z
user: login: danroth27
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-06T02:12:04Z
user: login: danroth27
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-06T03:06:10Z
user: login: maraf
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-07T06:52:41Z
user: login: maraf
body: Looks good to me 👍
Thank you!
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-08T08:00:12Z

Review comments:
diff_hunk: @@ -193,6 +193,44 @@ For examples of how to address the policy violation with Subresource Integrity (
 
 :::moniker-end
 
+:::moniker range=">= aspnetcore-10.0"
+
+### Fingerprint static assets in standalone Blazor WebAssembly apps
+
+In standalone Blazor WebAssembly apps during build/publish, the framework overrides placeholders in `index.html` with values computed during build to fingerprint static assets. A fingerprint is placed into the `blazor.webassembly.js` script file name, and an import map is generated for other .NET assets.
path: aspnetcore/blazor/fundamentals/static-files.md
user: login: maraf
body: Regarding the importmap, I can contain other assets as well, if they are javascript modules. Do have it covered for hosted mode using `<ImportMap />` component?

Related https://github.com/dotnet/runtime/issues/74815#issuecomment-2737171054.
created_at: 2025-03-27T14:13:59Z
author_association: MEMBER
diff_hunk: @@ -193,6 +193,44 @@ For examples of how to address the policy violation with Subresource Integrity (
 
 :::moniker-end
 
+:::moniker range=">= aspnetcore-10.0"
+
+### Fingerprint static assets in standalone Blazor WebAssembly apps
+
+In standalone Blazor WebAssembly apps during build/publish, the framework overrides placeholders in `index.html` with values computed during build to fingerprint static assets. A fingerprint is placed into the `blazor.webassembly.js` script file name, and an import map is generated for other .NET assets.
path: aspnetcore/blazor/fundamentals/static-files.md
user: login: guardrex
body: Yes, I think that point is covered here ...

https://learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/static-files?view=aspnetcore-10.0#import-maps

If this remark is too vague, I can change it to perhaps something like ...

```suggestion
In standalone Blazor WebAssembly apps during build/publish, the framework overrides placeholders in `index.html` with values computed during build to fingerprint static assets. A fingerprint is placed into the `blazor.webassembly.js` script file name, and an import map is generated for JavaScript modules.
```
created_at: 2025-03-27T14:21:51Z
author_association: COLLABORATOR
diff_hunk: @@ -193,6 +193,44 @@ For examples of how to address the policy violation with Subresource Integrity (
 
 :::moniker-end
 
+:::moniker range=">= aspnetcore-10.0"
+
+### Fingerprint static assets in standalone Blazor WebAssembly apps
+
+In standalone Blazor WebAssembly apps during build/publish, the framework overrides placeholders in `index.html` with values computed during build to fingerprint static assets. A fingerprint is placed into the `blazor.webassembly.js` script file name, and an import map is generated for other .NET assets.
path: aspnetcore/blazor/fundamentals/static-files.md
user: login: maraf
body: It works differently for hosted and standalone mode. In hosted mode all JS modules are soft fingerprinted, meaning that the file on disk don't have fingerprint and only aspnetcore server knows about URLs with fingerprint. We hard fingerprint .NET assets, meaning file on disk has a fingerprint. In standalone mode, there is no aspnetcore server and so only hard fingerprinted assets have fingerprint. And so if users want to fingerprint their JS modules and put them to importmap, they need to opt-in (as described in the linked comment). That is something we don't have convered with docs, am I right?
created_at: 2025-03-27T16:38:12Z
author_association: MEMBER
diff_hunk: @@ -193,6 +193,44 @@ For examples of how to address the policy violation with Subresource Integrity (
 
 :::moniker-end
 
+:::moniker range=">= aspnetcore-10.0"
+
+### Fingerprint static assets in standalone Blazor WebAssembly apps
+
+In standalone Blazor WebAssembly apps during build/publish, the framework overrides placeholders in `index.html` with values computed during build to fingerprint static assets. A fingerprint is placed into the `blazor.webassembly.js` script file name, and an import map is generated for other .NET assets.
path: aspnetcore/blazor/fundamentals/static-files.md
user: login: guardrex
body: Not covered. I just saw the linked remark.

What is meant by "hosted" or "hosted mode?" Do you mean the `.Client` project of a BWA, or does that mean the old hosted Blazor WebAssembly solution/template ... or both?
created_at: 2025-03-27T18:46:34Z
author_association: COLLABORATOR
diff_hunk: @@ -193,6 +193,44 @@ For examples of how to address the policy violation with Subresource Integrity (
 
 :::moniker-end
 
+:::moniker range=">= aspnetcore-10.0"
+
+### Fingerprint static assets in standalone Blazor WebAssembly apps
+
+In standalone Blazor WebAssembly apps during build/publish, the framework overrides placeholders in `index.html` with values computed during build to fingerprint static assets. A fingerprint is placed into the `blazor.webassembly.js` script file name, and an import map is generated for other .NET assets.
path: aspnetcore/blazor/fundamentals/static-files.md
user: login: maraf
body: Both
created_at: 2025-03-28T10:34:21Z
author_association: MEMBER
diff_hunk: @@ -28,34 +28,58 @@ We recommend the following conventions:
 
 ## Set the environment
 
-:::moniker range=">= aspnetcore-8.0"
-
 The environment is set using any of the following approaches:
 
-* Blazor Web App: Use any of the approaches described in <xref:fundamentals/environments> for general ASP.NET Core apps.
-* Blazor Web App or standalone Blazor WebAssembly: [Blazor start configuration](#set-the-client-side-environment-via-blazor-startup-configuration)
-* Standalone Blazor WebAssembly: [`Blazor-Environment` header](#set-the-client-side-environment-via-header)
-* Blazor Web App or standalone Blazor WebAssembly: [Azure App Service](#set-the-environment-for-azure-app-service)
+:::moniker range=">= aspnetcore-10.0"
+
+* Blazor Web App or Blazor Server: Use any of the approaches described in <xref:fundamentals/environments> for general ASP.NET Core apps.
+* Any Blazor app: [Blazor start configuration](#set-the-client-side-environment-via-blazor-startup-configuration)
+* Standalone Blazor WebAssembly: `<WasmApplicationEnvironmentName>` property
+
+On the client for a Blazor Web App, the environment is determined from the server via an HTML comment that developers don't interact with:
+
+```html
+<!--Blazor-WebAssembly:{"environmentName":"Development", ...}-->
+```
+
+For a standalone Blazor WebAssembly app, set the environment with the `<WasmApplicationEnvironmentName>` property in the app's project file (`.csproj`). The following example sets the `Staging` environment:
+
+```xml
+<WasmApplicationEnvironmentName>Staging</WasmApplicationEnvironmentName>
+```
+
+The default environment is `Development` when running the app locally and `Production` when the app is published.
path: aspnetcore/blazor/fundamentals/environments.md
user: login: maraf
body: The exact terminology would "for build" => Development; "for publish" => Production
created_at: 2025-04-02T15:10:08Z
author_association: MEMBER
diff_hunk: @@ -193,6 +196,62 @@ For examples of how to address the policy violation with Subresource Integrity (
 
 :::moniker-end
 
+:::moniker range=">= aspnetcore-10.0"
+
+### Fingerprint client-side static assets
path: aspnetcore/blazor/fundamentals/static-files.md
user: login: maraf
body: nit: Should be H2? Others like "Import map Content Security Policy (CSP) violations" and "ImportMap component" are H2
created_at: 2025-04-02T15:13:12Z
author_association: MEMBER
diff_hunk: @@ -193,6 +196,62 @@ For examples of how to address the policy violation with Subresource Integrity (
 
 :::moniker-end
 
+:::moniker range=">= aspnetcore-10.0"
+
+### Fingerprint client-side static assets
+
+In standalone Blazor WebAssembly apps during build/publish, the framework overrides placeholders in `index.html` with values computed during build to fingerprint static assets for client-side rendering (CSR). A [fingerprint](https://en.wikipedia.org/wiki/Fingerprint_(computing)) is placed into the `blazor.webassembly.js` script file name, and an import map is generated for other .NET assets.
+
+The following configuration must be present in the `wwwwoot/index.html` file of a standalone Blazor WebAssembly app to adopt fingerprinting:
+
+```html
+<head>
+    ...
+    <script type="importmap"></script>
+    ...
+</head>
+
+<body>
+    ...
+    <script src="_framework/blazor.webassembly#[.{fingerprint}].js"></script>
+    ...
+</body>
+
+</html>
+```
+
+In the project file (`.csproj`), the `<WriteImportMapToHtml>` property is set to `true`:
+
+```xml
+<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
+  <PropertyGroup>
+    ...
+    <WriteImportMapToHtml>true</WriteImportMapToHtml>
+    ...
+  </PropertyGroup>
+</Project>
+```
+
+For CSR in Blazor Web Apps (Interactive Auto or Interactive WebAssembly render modes), static asset server-side fingerprinting is enabled by adopting [Map Static Assets routing endpoint conventions (`MapStaticAssets`)](xref:fundamentals/map-static-files) and the [`ImportMap` component](xref:blazor/fundamentals/static-files#importmap-component).
path: aspnetcore/blazor/fundamentals/static-files.md
user: login: maraf
body: Should we mention `@Assets[""]` as well? 
created_at: 2025-04-02T15:14:23Z
author_association: MEMBER
diff_hunk: @@ -193,6 +196,62 @@ For examples of how to address the policy violation with Subresource Integrity (
 
 :::moniker-end
 
+:::moniker range=">= aspnetcore-10.0"
+
+### Fingerprint client-side static assets
+
+In standalone Blazor WebAssembly apps during build/publish, the framework overrides placeholders in `index.html` with values computed during build to fingerprint static assets for client-side rendering (CSR). A [fingerprint](https://en.wikipedia.org/wiki/Fingerprint_(computing)) is placed into the `blazor.webassembly.js` script file name, and an import map is generated for other .NET assets.
+
+The following configuration must be present in the `wwwwoot/index.html` file of a standalone Blazor WebAssembly app to adopt fingerprinting:
+
+```html
+<head>
+    ...
+    <script type="importmap"></script>
+    ...
+</head>
+
+<body>
+    ...
+    <script src="_framework/blazor.webassembly#[.{fingerprint}].js"></script>
+    ...
+</body>
+
+</html>
+```
+
+In the project file (`.csproj`), the `<WriteImportMapToHtml>` property is set to `true`:
+
+```xml
+<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
+  <PropertyGroup>
+    ...
+    <WriteImportMapToHtml>true</WriteImportMapToHtml>
+    ...
+  </PropertyGroup>
+</Project>
+```
+
+For CSR in Blazor Web Apps (Interactive Auto or Interactive WebAssembly render modes), static asset server-side fingerprinting is enabled by adopting [Map Static Assets routing endpoint conventions (`MapStaticAssets`)](xref:fundamentals/map-static-files) and the [`ImportMap` component](xref:blazor/fundamentals/static-files#importmap-component).
+
+To fingerprint additional JavaScript modules for CSR, use the `<StaticWebAssetFingerprintPattern>` property in the app's project file (`.csproj`).
path: aspnetcore/blazor/fundamentals/static-files.md
user: login: maraf
body: It's item, not a property
```xml
<ItemGroup>
   <StaticWebAssetFingerprintPattern ...
</ItemGroup>
```
created_at: 2025-04-02T15:15:12Z
author_association: MEMBER
diff_hunk: @@ -193,6 +196,62 @@ For examples of how to address the policy violation with Subresource Integrity (
 
 :::moniker-end
 
+:::moniker range=">= aspnetcore-10.0"
+
+### Fingerprint client-side static assets
path: aspnetcore/blazor/fundamentals/static-files.md
user: login: maraf
body: Me personal feeling is that this section is hard to understand, it's quite different between Blazor Web App & Blazor WebAssembly Standalone.

So my general question is probably:
Should we mix these modes in a single document?
Or should we focus the document on or the other mode?

I can see to ways of reading docs
- I read it like a book and probably want to learn about the same concepts for both modes
- I have an app and want to solve something, then I would appreciate to select the mode and read the details only for that mode

Mostly just my thoughts :)
created_at: 2025-04-02T15:20:31Z
author_association: MEMBER
diff_hunk: @@ -193,6 +196,62 @@ For examples of how to address the policy violation with Subresource Integrity (
 
 :::moniker-end
 
+:::moniker range=">= aspnetcore-10.0"
+
+### Fingerprint client-side static assets
path: aspnetcore/blazor/fundamentals/static-files.md
user: login: guardrex
body: I'll take a look.
created_at: 2025-04-02T15:24:51Z
author_association: COLLABORATOR
diff_hunk: @@ -193,6 +196,62 @@ For examples of how to address the policy violation with Subresource Integrity (
 
 :::moniker-end
 
+:::moniker range=">= aspnetcore-10.0"
+
+### Fingerprint client-side static assets
path: aspnetcore/blazor/fundamentals/static-files.md
user: login: guardrex
body: I'll break this into two sections on the next commit.
created_at: 2025-04-02T15:44:24Z
author_association: COLLABORATOR
diff_hunk: @@ -132,4 +132,74 @@ Also make this change in the *Routing* article at Line 1633.
 
 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 
+### Client-side fingerprinting
+
+Last year, the release of .NET 9 introduced [server-side fingerprinting](https://en.wikipedia.org/wiki/Fingerprint_(computing)) of static assets in Blazor Web Apps with the introduction of [Map Static Assets routing endpoint conventions (`MapStaticAssets`)](xref:fundamentals/map-static-files), the [`ImportMap` component](xref:blazor/fundamentals/static-files#importmap-component), and the <xref:Microsoft.AspNetCore.Components.ComponentBase.Assets?displayProperty=nameWithType> property (`@Assets["..."]`) to resolve fingerprinted JavaScript modules. For .NET 10, you can opt-into client-side fingerprinting of JavaScript modules for standalone Blazor WebAssembly apps.
+
+In standalone Blazor WebAssembly apps during build/publish, the framework overrides placeholders in `index.html` with values computed during build to fingerprint static assets. A fingerprint is placed into the `blazor.webassembly.js` script file name.
+
+The following changes must be made in the `wwwwoot/index.html` file to adopt the fingerprinting feature. The standalone Blazor WebAssembly project template will be updated to include these changes in an upcoming preview release:
+
+```diff
+<head>
+    ...
++   <script type="importmap"></script>
+</head>
+
+<body>
+    ...
+-   <script src="_framework/blazor.webassembly.js"></script>
++   <script src="_framework/blazor.webassembly#[.{fingerprint}].js"></script>
+</body>
+
+</html>
+```
+
+In the project file (`.csproj`), add the `<OverrideHtmlAssetPlaceholders>` property set to `true`:
path: aspnetcore/release-notes/aspnetcore-10/includes/blazor.md
user: login: danroth27
body: The property rename isn't in Preview 3. It's in Preview 4.
created_at: 2025-04-05T02:17:34Z
author_association: MEMBER
diff_hunk: @@ -132,4 +132,74 @@ Also make this change in the *Routing* article at Line 1633.
 
 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 
+### Client-side fingerprinting
+
+Last year, the release of .NET 9 introduced [server-side fingerprinting](https://en.wikipedia.org/wiki/Fingerprint_(computing)) of static assets in Blazor Web Apps with the introduction of [Map Static Assets routing endpoint conventions (`MapStaticAssets`)](xref:fundamentals/map-static-files), the [`ImportMap` component](xref:blazor/fundamentals/static-files#importmap-component), and the <xref:Microsoft.AspNetCore.Components.ComponentBase.Assets?displayProperty=nameWithType> property (`@Assets["..."]`) to resolve fingerprinted JavaScript modules. For .NET 10, you can opt-into client-side fingerprinting of JavaScript modules for standalone Blazor WebAssembly apps.
+
+In standalone Blazor WebAssembly apps during build/publish, the framework overrides placeholders in `index.html` with values computed during build to fingerprint static assets. A fingerprint is placed into the `blazor.webassembly.js` script file name.
+
+The following changes must be made in the `wwwwoot/index.html` file to adopt the fingerprinting feature. The standalone Blazor WebAssembly project template will be updated to include these changes in an upcoming preview release:
+
+```diff
+<head>
+    ...
++   <script type="importmap"></script>
+</head>
+
+<body>
+    ...
+-   <script src="_framework/blazor.webassembly.js"></script>
++   <script src="_framework/blazor.webassembly#[.{fingerprint}].js"></script>
+</body>
+
+</html>
+```
+
+In the project file (`.csproj`), add the `<OverrideHtmlAssetPlaceholders>` property set to `true`:
path: aspnetcore/release-notes/aspnetcore-10/includes/blazor.md
user: login: guardrex
body: ~I'll revert and place an issue in the morning.~

***Done!*** 👍  ... and tracked for Pre4.
created_at: 2025-04-05T02:24:11Z
author_association: COLLABORATOR
diff_hunk: @@ -132,4 +132,74 @@ Also make this change in the *Routing* article at Line 1633.
 
 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 
+### Client-side fingerprinting
+
+Last year, the release of .NET 9 introduced [server-side fingerprinting](https://en.wikipedia.org/wiki/Fingerprint_(computing)) of static assets in Blazor Web Apps with the introduction of [Map Static Assets routing endpoint conventions (`MapStaticAssets`)](xref:fundamentals/map-static-files), the [`ImportMap` component](xref:blazor/fundamentals/static-files#importmap-component), and the <xref:Microsoft.AspNetCore.Components.ComponentBase.Assets?displayProperty=nameWithType> property (`@Assets["..."]`) to resolve fingerprinted JavaScript modules. For .NET 10, you can opt-into client-side fingerprinting of JavaScript modules for standalone Blazor WebAssembly apps.
+
+In standalone Blazor WebAssembly apps during build/publish, the framework overrides placeholders in `index.html` with values computed during build to fingerprint static assets. A fingerprint is placed into the `blazor.webassembly.js` script file name.
+
+The following changes must be made in the `wwwwoot/index.html` file to adopt the fingerprinting feature. The standalone Blazor WebAssembly project template will be updated to include these changes in an upcoming preview release:
+
+```diff
+<head>
+    ...
++   <script type="importmap"></script>
+</head>
+
+<body>
+    ...
+-   <script src="_framework/blazor.webassembly.js"></script>
++   <script src="_framework/blazor.webassembly#[.{fingerprint}].js"></script>
+</body>
+
+</html>
+```
+
+In the project file (`.csproj`), add the `<OverrideHtmlAssetPlaceholders>` property set to `true`:
+
+```diff
+<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
+
+  <PropertyGroup>
+    <TargetFramework>net10.0</TargetFramework>
+    <Nullable>enable</Nullable>
+    <ImplicitUsings>enable</ImplicitUsings>
++   <OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
+  </PropertyGroup>
+</Project>
+```
+
+To fingerprint additional JS modules in standalone Blazor WebAssembly apps, use the `<StaticWebAssetFingerprintPattern>` property in the app's project file (`.csproj`).
+
+In the following example, a fingerprint is added for all developer-supplied `.mjs` files in the app:
+
+```xml
+<StaticWebAssetFingerprintPattern Include="JSModule" Pattern="*.mjs" 
+  Expression="#[.{fingerprint}]!" />
+```
+
+The files are automatically placed into the import map:
+
+* Automatically for Blazor Web App CSR.
+* When opting-into module fingerprinting in standalone Blazor WebAssembly apps per the preceding instructions.
+
+When resolving the import for JavaScript interop, the import map is used by the browser resolve fingerprinted files.
+
+### Set the environment in standalone Blazor WebAssembly apps
+
+The `Properties/launchSettings.json` file is no longer used to control the environment in standalone Blazor WebAssembly apps.
path: aspnetcore/release-notes/aspnetcore-10/includes/blazor.md
user: login: danroth27
body: @maraf This makes it sound like we made a breaking change here. Is the only impact on how you set the environment for standalone Blazor WebAssembly apps during development? Or is there also impact on existing ASP.NET Core hosted apps?
created_at: 2025-04-06T02:12:03Z
author_association: MEMBER
diff_hunk: @@ -132,4 +132,74 @@ Also make this change in the *Routing* article at Line 1633.
 
 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 
+### Client-side fingerprinting
+
+Last year, the release of .NET 9 introduced [server-side fingerprinting](https://en.wikipedia.org/wiki/Fingerprint_(computing)) of static assets in Blazor Web Apps with the introduction of [Map Static Assets routing endpoint conventions (`MapStaticAssets`)](xref:fundamentals/map-static-files), the [`ImportMap` component](xref:blazor/fundamentals/static-files#importmap-component), and the <xref:Microsoft.AspNetCore.Components.ComponentBase.Assets?displayProperty=nameWithType> property (`@Assets["..."]`) to resolve fingerprinted JavaScript modules. For .NET 10, you can opt-into client-side fingerprinting of JavaScript modules for standalone Blazor WebAssembly apps.
+
+In standalone Blazor WebAssembly apps during build/publish, the framework overrides placeholders in `index.html` with values computed during build to fingerprint static assets. A fingerprint is placed into the `blazor.webassembly.js` script file name.
+
+The following changes must be made in the `wwwwoot/index.html` file to adopt the fingerprinting feature. The standalone Blazor WebAssembly project template will be updated to include these changes in an upcoming preview release:
+
+```diff
+<head>
+    ...
++   <script type="importmap"></script>
+</head>
+
+<body>
+    ...
+-   <script src="_framework/blazor.webassembly.js"></script>
++   <script src="_framework/blazor.webassembly#[.{fingerprint}].js"></script>
+</body>
+
+</html>
+```
+
+In the project file (`.csproj`), add the `<WriteImportMapToHtml>` property set to `true`:
+
+```diff
+<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
+
+  <PropertyGroup>
+    <TargetFramework>net10.0</TargetFramework>
+    <Nullable>enable</Nullable>
+    <ImplicitUsings>enable</ImplicitUsings>
++   <WriteImportMapToHtml>true</WriteImportMapToHtml>
+  </PropertyGroup>
+</Project>
+```
+
+To fingerprint additional JS modules in standalone Blazor WebAssembly apps, use the `<StaticWebAssetFingerprintPattern>` property in the app's project file (`.csproj`).
+
+In the following example, a fingerprint is added for all developer-supplied `.mjs` files in the app:
+
+```xml
+<StaticWebAssetFingerprintPattern Include="JSModule" Pattern="*.mjs" 
+  Expression="#[.{fingerprint}]!" />
+```
+
+The files are automatically placed into the import map:
+
+* Automatically for Blazor Web App CSR.
+* When opting-into module fingerprinting in standalone Blazor WebAssembly apps per the preceding instructions.
+
+When resolving the import for JavaScript interop, the import map is used by the browser resolve fingerprinted files.
+
+### Set the environment in standalone Blazor WebAssembly apps
+
+The `Properties/launchSettings.json` file is no longer used to control the environment in standalone Blazor WebAssembly apps.
+
+Starting in .NET 10, set the environment with the `<WasmApplicationEnvironmentName>` property in the app's project file (`.csproj`).
+
+The following example sets the app's environment to `Staging`:
+
+```xml
+<WasmApplicationEnvironmentName>Staging</WasmApplicationEnvironmentName>
+```
+
+As usual, the default environments are:
path: aspnetcore/release-notes/aspnetcore-10/includes/blazor.md
user: login: danroth27
body: From a runtime perspective the ASP.NET Core environment is actually Production by default, so let's not say "as usual" here.

```suggestion
The default environments are:
```
created_at: 2025-04-06T03:06:10Z
author_association: MEMBER
diff_hunk: @@ -132,4 +132,74 @@ Also make this change in the *Routing* article at Line 1633.
 
 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 
+### Client-side fingerprinting
+
+Last year, the release of .NET 9 introduced [server-side fingerprinting](https://en.wikipedia.org/wiki/Fingerprint_(computing)) of static assets in Blazor Web Apps with the introduction of [Map Static Assets routing endpoint conventions (`MapStaticAssets`)](xref:fundamentals/map-static-files), the [`ImportMap` component](xref:blazor/fundamentals/static-files#importmap-component), and the <xref:Microsoft.AspNetCore.Components.ComponentBase.Assets?displayProperty=nameWithType> property (`@Assets["..."]`) to resolve fingerprinted JavaScript modules. For .NET 10, you can opt-into client-side fingerprinting of JavaScript modules for standalone Blazor WebAssembly apps.
+
+In standalone Blazor WebAssembly apps during build/publish, the framework overrides placeholders in `index.html` with values computed during build to fingerprint static assets. A fingerprint is placed into the `blazor.webassembly.js` script file name.
+
+The following changes must be made in the `wwwwoot/index.html` file to adopt the fingerprinting feature. The standalone Blazor WebAssembly project template will be updated to include these changes in an upcoming preview release:
+
+```diff
+<head>
+    ...
++   <script type="importmap"></script>
+</head>
+
+<body>
+    ...
+-   <script src="_framework/blazor.webassembly.js"></script>
++   <script src="_framework/blazor.webassembly#[.{fingerprint}].js"></script>
+</body>
+
+</html>
+```
+
+In the project file (`.csproj`), add the `<OverrideHtmlAssetPlaceholders>` property set to `true`:
+
+```diff
+<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
+
+  <PropertyGroup>
+    <TargetFramework>net10.0</TargetFramework>
+    <Nullable>enable</Nullable>
+    <ImplicitUsings>enable</ImplicitUsings>
++   <OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
+  </PropertyGroup>
+</Project>
+```
+
+To fingerprint additional JS modules in standalone Blazor WebAssembly apps, use the `<StaticWebAssetFingerprintPattern>` property in the app's project file (`.csproj`).
+
+In the following example, a fingerprint is added for all developer-supplied `.mjs` files in the app:
+
+```xml
+<StaticWebAssetFingerprintPattern Include="JSModule" Pattern="*.mjs" 
+  Expression="#[.{fingerprint}]!" />
+```
+
+The files are automatically placed into the import map:
+
+* Automatically for Blazor Web App CSR.
+* When opting-into module fingerprinting in standalone Blazor WebAssembly apps per the preceding instructions.
+
+When resolving the import for JavaScript interop, the import map is used by the browser resolve fingerprinted files.
+
+### Set the environment in standalone Blazor WebAssembly apps
+
+The `Properties/launchSettings.json` file is no longer used to control the environment in standalone Blazor WebAssembly apps.
path: aspnetcore/release-notes/aspnetcore-10/includes/blazor.md
user: login: maraf
body: Only standalone mode during development affected
created_at: 2025-04-07T06:52:41Z
author_association: MEMBER

PR comments:
user: login: guardrex
created_at: 2025-03-27T15:51:03Z
body: @maraf ... To avoid a merge conflict in the *What's New* file, I'll just combine the coverage here. This PR now covers for Blazor WASM ...

* Fingerprinting
* `WasmApplicationEnvironmentName` for environment control

I think I'm going to need more information tho ... what's happening with the `Blazor-Environment` header for >=10.0? Is that still how server-side is communicating the environment to the client-side?
author_association: COLLABORATOR
user: login: maraf
created_at: 2025-03-28T13:05:18Z
body: > I think I'm going to need more information tho ... what's happening with the `Blazor-Environment` header for >=10.0? Is that still how server-side is communicating the environment to the client-side?

That header doesn't exist any more, because of the `blazor.boot.json => dotnet.boot.js` we don't have a way to read it.
In server-side scenario, the value is part of the initial document response, encoded as HTML comment https://github.com/dotnet/aspnetcore/pull/60714 (I think is kind of internal thing as it shouldn't effect users by any means).
In standalone scenario, we don't have a way to read the value that is part of the launchSettings.json and that was used when launching DevServer. So instead we have the MSBuild property.
author_association: MEMBER
user: login: guardrex
created_at: 2025-03-28T13:29:41Z
body: I'm working on this now, and it's a bit tricky to explain without getting into implementation details. AFIACT, "hard" and "soft" fingerprinting aren't industry concepts. They seem to be convenient ways to describe implementation details of the framework. I'm trying to write this up without referring to them.

Hopefully, I'll have this updated shortly, and then we can see just how badly 🙈 it has turned out 😆 ... and then try to fix it from there.
author_association: COLLABORATOR
user: login: guardrex
created_at: 2025-03-28T15:29:18Z
body: I'll pick back up with this on Monday morning. I'm going to hit a 🧠🔥 *brain fry* soon 😨, and it's been a busy week here 😅 with only two hours left to work.

I'm finding the "hard" versus "soft" fingerprinting concept difficult to understand and explain; and like I said earlier, "hard" and "soft" fingerprinting seem to only be convenient descriptions for Blazor and not industry terms.

Side note: I've been avoiding "hosted" Blazor phrasing because of how pervasive the Hosted Blazor WebAssembly" project type/template was prior to .NET 8's release. As you said, "hosted" here also refers to the `.Client` project of a BWA, and a BWA isn't exactly a hosted Blazor WebAssembly app of .NET 7 or earlier.

Can we explain fingerprinting behaviors and opt-in requirements on the basis of the project type and render modes that the dev is working with without referring to "hard"/"soft" fingerprinting?

* BWA SSR: Automatic module fingerprinting with `MapStaticAssets`/`ImportMap`, including dev-supplied modules (***IF*** they don't define and assign a custom `ImportMapDefinition`).
* BWA Auto: ???
* BWA CSR: ???
* Standalone Blazor WebAssembly: Devs must opt-in to module fingerprinting with the instructions you provided ... and they must opt-into dev-supplied module fingerprinting with `<StaticWebAssetFingerprintPattern ... />`.

... and we probably don't need to talk about the old hosted Blazor WebAssembly setup, since we almost completely dropped it from documentation >=8.0 and now recommend BWA global CSR.
author_association: COLLABORATOR
user: login: guardrex
created_at: 2025-03-28T15:52:04Z
body: The `Blazor-Environment` header aspects have been addressed, and that's looking good thus far. I'll double-check the updates on the last commit on Monday morning.

[***Removed remarks***]

🤔 ***I think I was *overthinking* what this needs to say.*** On the last commit, I placed my current understanding. See if that's at least headed in the right direction for module fingerprinting.

... actually the last two commits because I had to go back into the CSR reference docs and describe CSR for BWAs and CSR for standalone Blazor WebAssembly .......... well ...... my interpretations on them. We'll see where it's all going sideways when you look at it 🙈😆. 


author_association: COLLABORATOR
user: login: guardrex
created_at: 2025-03-31T13:18:22Z
body: @maraf ... I only found NIT updates to make this morning.

AFAICT, we can avoid "hard" and "soft" fingerprinting details.

I'm 👂 to learn if what's here is accurate.
author_association: COLLABORATOR
user: login: maraf
created_at: 2025-04-02T15:06:45Z
body: A rename is coming your way https://github.com/dotnet/sdk/pull/47915
author_association: MEMBER
user: login: guardrex
created_at: 2025-04-02T15:23:05Z
body: Update made. Let me know as soon as we can merge this. It's blocking other work just a little bit in terms of the *What's New* INCLUDE. Javier's *Declaratively persist state* updates are next and will touch the INCLUDE file.
author_association: COLLABORATOR
user: login: maraf
created_at: 2025-04-02T15:25:12Z
body: Now that I see the docs, the property `WasmApplicationEnvironmentName` should actually be `WasmApplicationEnvironment`, am I right?
author_association: MEMBER
user: login: guardrex
created_at: 2025-04-02T15:25:49Z
body: > Now that I see the docs, the property WasmApplicationEnvironmentName should actually be WasmApplicationEnvironment, am I right?

I'll check it.
author_association: COLLABORATOR
user: login: guardrex
created_at: 2025-04-02T15:40:37Z
body: Looks like it has "Name" on it ...

https://github.com/maraf/runtime/blob/main/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets#L333
author_association: COLLABORATOR
user: login: guardrex
created_at: 2025-04-02T15:53:36Z
body: btw ... There are 41 references to `blazor.boot.json` around the docs. I have a way to address them uniformly that might work with a section that calls out the name of the boot file and cross-links to that section for devs to get the right file name.

The problem with versioning all of the content for the boot file name change is that our versioning tools are weak for these types of large-scale changes (i.e., we don't have *Version-by-file* versioning 😢). It's best to take care of this on a different PR, so I've opened #35125 to address it after this PR is merged.
author_association: COLLABORATOR
user: login: guardrex
created_at: 2025-04-02T16:11:13Z
body: This has the updates on it now.

I split the BWA guidance from the standalone WASM guidance.
author_association: COLLABORATOR
user: login: maraf
created_at: 2025-04-03T08:42:48Z
body: > Looks like it has "Name" on it ...
> 
> https://github.com/maraf/runtime/blob/main/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets#L333

Yes, it has and I'm questioning my choice :) It looks like all other places in Blazor don't have it

> Update made. Let me know as soon as we can merge this. It's blocking other work just a little bit in terms of the What's New INCLUDE. Javier's Declaratively persist state updates are next and will touch the INCLUDE file.

The rename will be preview4
author_association: MEMBER
user: login: guardrex
created_at: 2025-04-03T14:09:20Z
body: > I'm questioning my choice

# 😆 

I was wondering why ***you*** were asking ***me*** ... *a goober docs dinosaur* 🦖🙈🤣. I'm not much help when it comes to framework design.

Having "Name" on it seems to line up OK with ...

https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting.ihostenvironment?view=net-9.0-pp

***However,*** "Name" wasn't used for the environment property here ...

https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.webassembly.hosting.iwebassemblyhostenvironment?view=aspnetcore-9.0

***And,*** it wasn't used for the `Blazor-Environment` header, which could've been the "`Blazor-Environment-Name`" header.

***And,*** it wasn't used for `Blazor.start()` ... `{ environment: "..." }`, which could've been `{ environmentName: "..." }`.
author_association: COLLABORATOR
user: login: guardrex
created_at: 2025-04-04T15:15:58Z
body: @maraf ... Is this PR ready to merge?

And, WRT ...

> The rename will be preview4

Do you mean `WasmApplicationEnvironmentName` to `WasmApplicationEnvironment`? If so, I'll open a Pre4 issue to make sure that it gets updated.
author_association: COLLABORATOR
user: login: guardrex
created_at: 2025-04-07T11:52:32Z
body: @danroth27 @maraf ... Are we good to go on this one?

* Next, I'll work the boot file name change (`blazor.boot.json` 👉 `dotnet.boot.js`), but I'll wait on your, @maraf, observations on #35125 before setting up the PR.
* Then, I'll work in Javier's declarative state persistence piece.

That should wrap up Pre3 work, but I'll check one more time to see if I missed anything.
author_association: COLLABORATOR