.
-
-In the `.Client` project, add the `CultureSelector` component to the `MainLayout` component. Place the following markup inside the closing `` tag in the `Layout/MainLayout.razor` file:
+Place the following markup in the header content of the `Components/Layout/MainLayout.razor` file:
```razor
-
-
-
+
```
In the `.Client` project, place the following `CultureClient` component to study how globalization works for CSR components.
@@ -1309,9 +1359,13 @@ In the `.Client` project, place the following `CultureServer` component to study
}
```
-Use the `CultureExample1` component shown in the [Demonstration component](#demonstration-component) section to study how globalization works for a component that inherits the global Auto render mode. Add the `CultureExample1` component to the `.Client` project's `Pages` folder.
+Use the `CultureExample1` component shown in the [Demonstration component](#demonstration-component) section to study how globalization works for a component that inherits the global Auto render mode. Add the `CultureExample1` component to the `.Client` project's `Pages` folder. At the top of the component, specify the Interactive Auto render mode:
-Add the `CultureClient`, `CultureServer`, and `CultureExample1` components to the sidebar navigation in `Layout/NavMenu.razor`:
+```razor
+@rendermode InteractiveAuto
+```
+
+Add the `CultureClient`, `CultureServer`, and `CultureExample1` components to the sidebar navigation in `Components/Layout/NavMenu.razor` of the server project:
```razor
@@ -1408,56 +1462,37 @@ Add the following `@code` block to the bottom of the `App` component file:
}
```
-If the server project isn't configured to process controller actions:
+To provide UI to allow a user to select a culture, use a *redirect-based approach* with a localization cookie. The app persists the user's selected culture via a redirect to a Minimal API endpoint. The endpoint sets the user's selected culture into a cookie and redirects the user back to the original URI. The process is similar to what happens in a web app when a user attempts to access a secure resource, where the user is redirected to a sign-in page and then redirected back to the original resource.
-* Add MVC services by calling on the service collection in the `Program` file:
-
- ```csharp
- builder.Services.AddControllers();
- ```
-
-* Add controller endpoint routing in the `Program` file by calling on the (`app`):
-
- ```csharp
- app.MapControllers();
- ```
-
-To allow a user to select a culture for SSR components, use a *redirect-based approach* with a localization cookie. The app persists the user's selected culture via a redirect to a controller. The controller sets the user's selected culture into a cookie and redirects the user back to the original URI. The process is similar to what happens in a web app when a user attempts to access a secure resource, where the user is redirected to a sign-in page and then redirected back to the original resource.
-
-`Controllers/CultureController.cs`:
+At the top of the `Program` file, add the following `using` statements for the required namespaces:
```csharp
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
+```
-[Route("[controller]/[action]")]
-public class CultureController : Controller
+In the request processing pipeline of the app's `Program` file:
+
+```csharp
+app.MapGet("/Culture/Set", (
+ [FromQuery] string? culture,
+ [FromQuery] string redirectUri,
+ HttpContext context) =>
{
- public IActionResult Set(string culture, string redirectUri)
+ if (culture != null)
{
- if (culture != null)
- {
- HttpContext.Response.Cookies.Append(
- CookieRequestCultureProvider.DefaultCookieName,
- CookieRequestCultureProvider.MakeCookieValue(
- new RequestCulture(culture, culture)));
- }
-
- return LocalRedirect(redirectUri);
+ context.Response.Cookies.Append(
+ CookieRequestCultureProvider.DefaultCookieName,
+ CookieRequestCultureProvider.MakeCookieValue(
+ new RequestCulture(culture, culture)));
}
-}
+
+ return Results.LocalRedirect(redirectUri);
+});
```
> [!WARNING]
-> Use the action result, as shown in the preceding example, to prevent open redirect attacks. For more information, see .
-
-### Interactive Auto components
-
-The guidance in this section also works for components in apps that adopt per-page/component rendering and specify the Interactive Auto render mode:
-
-```razor
-@rendermode InteractiveAuto
-```
+> Use the result, as shown in the preceding example, to prevent open redirect attacks. For more information, see .
:::moniker-end
diff --git a/aspnetcore/security/preventing-open-redirects.md b/aspnetcore/security/preventing-open-redirects.md
index 86a9afa7a0a3..c1b00870269b 100644
--- a/aspnetcore/security/preventing-open-redirects.md
+++ b/aspnetcore/security/preventing-open-redirects.md
@@ -1,9 +1,10 @@
---
title: Prevent open redirect attacks in ASP.NET Core
+ai-usage: ai-assisted
author: ardalis
description: Shows how to prevent open redirect attacks against an ASP.NET Core app
ms.author: tdykstra
-ms.date: 07/07/2017
+ms.date: 08/25/2026
uid: security/preventing-open-redirects
---
# Prevent open redirect attacks in ASP.NET Core
@@ -35,11 +36,20 @@ In addition to login pages, some sites provide redirect pages or endpoints. Imag
## Protecting against open redirect attacks
-When developing web applications, treat all user-provided data as untrustworthy. If your application has functionality that redirects the user based on the contents of the URL, ensure that such redirects are only done locally within your app (or to a known URL, not any URL that may be supplied in the querystring).
+When developing web applications, treat all user-provided data as untrustworthy. If your application has functionality that redirects the user based on the contents of the URL, ensure that such redirects are only done locally within your app (or to a known URL, not any URL that may be supplied in the querystring).
### LocalRedirect
-Use the `LocalRedirect` helper method from the base `Controller` class:
+Use the result in a Minimal API endpoint:
+
+```csharp
+app.MapGet("/SomeEndpoint", ([FromQuery] string redirectUri) =>
+{
+ return Results.LocalRedirect(redirectUri);
+});
+```
+
+Use the helper method in MVC actions:
```csharp
public IActionResult SomeAction(string redirectUrl)
@@ -48,7 +58,7 @@ public IActionResult SomeAction(string redirectUrl)
}
```
-`LocalRedirect` will throw an exception if a non-local URL is specified. Otherwise, it behaves just like the `Redirect` method.
+`LocalRedirect` throws an exception if a non-local URL is specified. Otherwise, it behaves just like the `Redirect` method.
### IUrlHelper.IsLocalUrl
From 14c36b3b27833aacf96c0a0f16473393c8a8103a Mon Sep 17 00:00:00 2001
From: guardrex <1622880+guardrex@users.noreply.github.com>
Date: Tue, 25 Aug 2026 10:18:23 -0400
Subject: [PATCH 2/6] Updates
---
.../blazor/globalization-localization.md | 27 ++++++++++---------
.../security/preventing-open-redirects.md | 2 +-
2 files changed, 15 insertions(+), 14 deletions(-)
diff --git a/aspnetcore/blazor/globalization-localization.md b/aspnetcore/blazor/globalization-localization.md
index 79b4f1eb775f..a9b1278bec59 100644
--- a/aspnetcore/blazor/globalization-localization.md
+++ b/aspnetcore/blazor/globalization-localization.md
@@ -654,7 +654,8 @@ The following `CultureSelector` component shows how to perform the following act
private async Task ApplySelectedCultureAsync()
{
- if (CultureInfo.CurrentCulture != selectedCulture)
+ if (selectedCulture is not null &&
+ CultureInfo.CurrentCulture != selectedCulture)
{
await JS.InvokeVoidAsync("blazorCulture.set", selectedCulture!.Name);
@@ -704,7 +705,8 @@ The following `CultureSelector` component shows how to perform the following act
{
selectedCulture = CultureInfo.GetCultureInfo((string)args.Value!);
- if (CultureInfo.CurrentCulture != selectedCulture)
+ if (selectedCulture is not null &&
+ CultureInfo.CurrentCulture != selectedCulture)
{
await JS.InvokeVoidAsync("blazorCulture.set", selectedCulture!.Name);
@@ -860,9 +862,7 @@ using Microsoft.AspNetCore.Mvc;
In the request processing pipeline of the app's `Program` file:
```csharp
-app.MapGet("/Culture/Set", (
- [FromQuery] string? culture,
- [FromQuery] string redirectUri,
+app.MapGet("/Culture/Set", ([FromQuery] string culture, string redirectUri,
HttpContext context) =>
{
if (culture != null)
@@ -981,7 +981,8 @@ The following `CultureSelector` component shows how to call the `Set` endpoint w
private async Task ApplySelectedCultureAsync()
{
- if (CultureInfo.CurrentCulture != selectedCulture)
+ if (selectedCulture is not null &&
+ CultureInfo.CurrentCulture != selectedCulture)
{
var uri = new Uri(Navigation.Uri)
.GetComponents(UriComponents.PathAndQuery, UriFormat.Unescaped);
@@ -1036,7 +1037,8 @@ The following `CultureSelector` component shows how to call the `Set` endpoint w
{
selectedCulture = CultureInfo.GetCultureInfo((string)args.Value!);
- if (CultureInfo.CurrentCulture != selectedCulture)
+ if (selectedCulture is not null &&
+ CultureInfo.CurrentCulture != selectedCulture)
{
var uri = new Uri(Navigation.Uri)
.GetComponents(UriComponents.PathAndQuery, UriFormat.Unescaped);
@@ -1172,7 +1174,7 @@ Add the following `CultureSelector` component to the `.Client` project in a `Sha
The component adopts the following approaches to work for either SSR or CSR components:
* The display name of each available culture in the dropdown list is provided by a dictionary because client-side globalization data includes localized text of culture display names that server-side globalization data provides. For example, server-side localization displays `English (United States)` when `en-US` is the culture and `Ingles ()` when a different culture is used. Because localization of the culture display names isn't available for Blazor WebAssembly globalization, the display name for United States English on the client for any loaded culture is just `en-US`. Using a custom dictionary permits the component to at least display full English culture names.
-* When user changes the culture, JS interop sets the culture in local browser storage and a Minimal API endpoint updates the localization cookie with the culture. The Minimal API endpoint is added to the app later in the [Server project updates](#server-project-updates) section.
+* When the user changes the culture, JS interop sets the culture in local browser storage and a Minimal API endpoint updates the localization cookie with the culture. The Minimal API endpoint is added to the app later in the [Server project updates](#server-project-updates) section.
`Shared/CultureSelector.razor`:
@@ -1217,7 +1219,8 @@ The component adopts the following approaches to work for either SSR or CSR comp
private async Task ApplySelectedCultureAsync()
{
- if (CultureInfo.CurrentCulture != selectedCulture)
+ if (selectedCulture is not null &&
+ CultureInfo.CurrentCulture != selectedCulture)
{
await JS.InvokeVoidAsync("blazorCulture.set", selectedCulture!.Name);
@@ -1237,7 +1240,7 @@ The component adopts the following approaches to work for either SSR or CSR comp
> [!NOTE]
> For more information on , see .
-In the server project, add the `CultureSelector` component to the `MainLayout` component. First, add the namespace for the `.Client` project's `Shared` folder:
+In the server project's imports file (`_Imports.razor`), add the namespace for the `.Client` project's `Shared` folder (update the namespace to match your app):
```razor
@using BlazorSample.Client.Shared
@@ -1474,9 +1477,7 @@ using Microsoft.AspNetCore.Mvc;
In the request processing pipeline of the app's `Program` file:
```csharp
-app.MapGet("/Culture/Set", (
- [FromQuery] string? culture,
- [FromQuery] string redirectUri,
+app.MapGet("/Culture/Set", (string culture, string redirectUri,
HttpContext context) =>
{
if (culture != null)
diff --git a/aspnetcore/security/preventing-open-redirects.md b/aspnetcore/security/preventing-open-redirects.md
index c1b00870269b..d9dee5acae98 100644
--- a/aspnetcore/security/preventing-open-redirects.md
+++ b/aspnetcore/security/preventing-open-redirects.md
@@ -43,7 +43,7 @@ When developing web applications, treat all user-provided data as untrustworthy.
Use the result in a Minimal API endpoint:
```csharp
-app.MapGet("/SomeEndpoint", ([FromQuery] string redirectUri) =>
+app.MapGet("/SomeEndpoint", (string redirectUri) =>
{
return Results.LocalRedirect(redirectUri);
});
From 2b8ee24ae12ec69961b5c94266831a57eb75d871 Mon Sep 17 00:00:00 2001
From: guardrex <1622880+guardrex@users.noreply.github.com>
Date: Tue, 25 Aug 2026 10:33:35 -0400
Subject: [PATCH 3/6] Updates
---
aspnetcore/blazor/globalization-localization.md | 14 +++++++-------
aspnetcore/security/preventing-open-redirects.md | 2 ++
2 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/aspnetcore/blazor/globalization-localization.md b/aspnetcore/blazor/globalization-localization.md
index a9b1278bec59..cd14b7c7a26f 100644
--- a/aspnetcore/blazor/globalization-localization.md
+++ b/aspnetcore/blazor/globalization-localization.md
@@ -657,7 +657,7 @@ The following `CultureSelector` component shows how to perform the following act
if (selectedCulture is not null &&
CultureInfo.CurrentCulture != selectedCulture)
{
- await JS.InvokeVoidAsync("blazorCulture.set", selectedCulture!.Name);
+ await JS.InvokeVoidAsync("blazorCulture.set", selectedCulture.Name);
Navigation.NavigateTo(Navigation.Uri, forceLoad: true);
}
@@ -708,7 +708,7 @@ The following `CultureSelector` component shows how to perform the following act
if (selectedCulture is not null &&
CultureInfo.CurrentCulture != selectedCulture)
{
- await JS.InvokeVoidAsync("blazorCulture.set", selectedCulture!.Name);
+ await JS.InvokeVoidAsync("blazorCulture.set", selectedCulture.Name);
Navigation.NavigateTo(Navigation.Uri, forceLoad: true);
}
@@ -862,10 +862,10 @@ using Microsoft.AspNetCore.Mvc;
In the request processing pipeline of the app's `Program` file:
```csharp
-app.MapGet("/Culture/Set", ([FromQuery] string culture, string redirectUri,
+app.MapGet("/Culture/Set", (string? culture, string redirectUri = "/",
HttpContext context) =>
{
- if (culture != null)
+ if (!string.IsNullOrWhiteSpace(culture))
{
context.Response.Cookies.Append(
CookieRequestCultureProvider.DefaultCookieName,
@@ -1222,7 +1222,7 @@ The component adopts the following approaches to work for either SSR or CSR comp
if (selectedCulture is not null &&
CultureInfo.CurrentCulture != selectedCulture)
{
- await JS.InvokeVoidAsync("blazorCulture.set", selectedCulture!.Name);
+ await JS.InvokeVoidAsync("blazorCulture.set", selectedCulture.Name);
var uri = new Uri(Navigation.Uri)
.GetComponents(UriComponents.PathAndQuery, UriFormat.Unescaped);
@@ -1477,10 +1477,10 @@ using Microsoft.AspNetCore.Mvc;
In the request processing pipeline of the app's `Program` file:
```csharp
-app.MapGet("/Culture/Set", (string culture, string redirectUri,
+app.MapGet("/Culture/Set", (string? culture, string redirectUri = "/",
HttpContext context) =>
{
- if (culture != null)
+ if (!string.IsNullOrWhiteSpace(culture))
{
context.Response.Cookies.Append(
CookieRequestCultureProvider.DefaultCookieName,
diff --git a/aspnetcore/security/preventing-open-redirects.md b/aspnetcore/security/preventing-open-redirects.md
index d9dee5acae98..571c4b5c8bbd 100644
--- a/aspnetcore/security/preventing-open-redirects.md
+++ b/aspnetcore/security/preventing-open-redirects.md
@@ -45,6 +45,8 @@ Use the result in a Mi
```csharp
app.MapGet("/SomeEndpoint", (string redirectUri) =>
{
+ ...
+
return Results.LocalRedirect(redirectUri);
});
```
From 5ab3347a71de5065300d64a72d61773adf500bb3 Mon Sep 17 00:00:00 2001
From: guardrex <1622880+guardrex@users.noreply.github.com>
Date: Tue, 25 Aug 2026 10:43:43 -0400
Subject: [PATCH 4/6] Updates
---
aspnetcore/blazor/globalization-localization.md | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/aspnetcore/blazor/globalization-localization.md b/aspnetcore/blazor/globalization-localization.md
index cd14b7c7a26f..12f1bb962f36 100644
--- a/aspnetcore/blazor/globalization-localization.md
+++ b/aspnetcore/blazor/globalization-localization.md
@@ -654,7 +654,7 @@ The following `CultureSelector` component shows how to perform the following act
private async Task ApplySelectedCultureAsync()
{
- if (selectedCulture is not null &&
+ if (selectedCulture is not null &&
CultureInfo.CurrentCulture != selectedCulture)
{
await JS.InvokeVoidAsync("blazorCulture.set", selectedCulture.Name);
@@ -705,7 +705,7 @@ The following `CultureSelector` component shows how to perform the following act
{
selectedCulture = CultureInfo.GetCultureInfo((string)args.Value!);
- if (selectedCulture is not null &&
+ if (selectedCulture is not null &&
CultureInfo.CurrentCulture != selectedCulture)
{
await JS.InvokeVoidAsync("blazorCulture.set", selectedCulture.Name);
@@ -852,11 +852,10 @@ For information on ordering the localization middleware in the middleware pipeli
To provide UI to allow a user to select a culture, use a *redirect-based approach* with a localization cookie. The app persists the user's selected culture via a redirect to a Minimal API endpoint. The endpoint sets the user's selected culture into a cookie and redirects the user back to the original URI. The process is similar to what happens in a web app when a user attempts to access a secure resource, where the user is redirected to a sign-in page and then redirected back to the original resource.
-At the top of the `Program` file, add the following `using` statements for the required namespaces:
+At the top of the `Program` file, add the following `using` statement for the required namespace:
```csharp
using Microsoft.AspNetCore.Localization;
-using Microsoft.AspNetCore.Mvc;
```
In the request processing pipeline of the app's `Program` file:
@@ -981,7 +980,7 @@ The following `CultureSelector` component shows how to call the `Set` endpoint w
private async Task ApplySelectedCultureAsync()
{
- if (selectedCulture is not null &&
+ if (selectedCulture is not null &&
CultureInfo.CurrentCulture != selectedCulture)
{
var uri = new Uri(Navigation.Uri)
@@ -1037,7 +1036,7 @@ The following `CultureSelector` component shows how to call the `Set` endpoint w
{
selectedCulture = CultureInfo.GetCultureInfo((string)args.Value!);
- if (selectedCulture is not null &&
+ if (selectedCulture is not null &&
CultureInfo.CurrentCulture != selectedCulture)
{
var uri = new Uri(Navigation.Uri)
@@ -1219,7 +1218,7 @@ The component adopts the following approaches to work for either SSR or CSR comp
private async Task ApplySelectedCultureAsync()
{
- if (selectedCulture is not null &&
+ if (selectedCulture is not null &&
CultureInfo.CurrentCulture != selectedCulture)
{
await JS.InvokeVoidAsync("blazorCulture.set", selectedCulture.Name);
@@ -1467,11 +1466,10 @@ Add the following `@code` block to the bottom of the `App` component file:
To provide UI to allow a user to select a culture, use a *redirect-based approach* with a localization cookie. The app persists the user's selected culture via a redirect to a Minimal API endpoint. The endpoint sets the user's selected culture into a cookie and redirects the user back to the original URI. The process is similar to what happens in a web app when a user attempts to access a secure resource, where the user is redirected to a sign-in page and then redirected back to the original resource.
-At the top of the `Program` file, add the following `using` statements for the required namespaces:
+At the top of the `Program` file, add the following `using` statement for the required namespace:
```csharp
using Microsoft.AspNetCore.Localization;
-using Microsoft.AspNetCore.Mvc;
```
In the request processing pipeline of the app's `Program` file:
From 86768e43dee80ac1221b5e1e93f17bf7978aac1a Mon Sep 17 00:00:00 2001
From: guardrex <1622880+guardrex@users.noreply.github.com>
Date: Wed, 26 Aug 2026 10:51:58 -0400
Subject: [PATCH 5/6] Updates
---
.../blazor/globalization-localization.md | 71 +++++++++++++------
.../security/preventing-open-redirects.md | 4 +-
2 files changed, 53 insertions(+), 22 deletions(-)
diff --git a/aspnetcore/blazor/globalization-localization.md b/aspnetcore/blazor/globalization-localization.md
index 12f1bb962f36..2c04b2c8a809 100644
--- a/aspnetcore/blazor/globalization-localization.md
+++ b/aspnetcore/blazor/globalization-localization.md
@@ -5,7 +5,7 @@ author: guardrex
description: Learn how to render globalized and localized content to users in different cultures and languages.
monikerRange: '>= aspnetcore-3.1'
ms.author: wpickett
-ms.date: 08/25/2026
+ms.date: 08/26/2026
uid: blazor/globalization-localization
---
# ASP.NET Core Blazor globalization and localization
@@ -277,6 +277,8 @@ Optionally, add a menu item to the navigation in the `NavMenu` component (`NavMe
## Dynamically set the culture from the `Accept-Language` header
+*This section applies to server-side and client-side Blazor apps.*
+
Add the [`Microsoft.Extensions.Localization` package](https://www.nuget.org/packages/Microsoft.Extensions.Localization) to the app.
The [`Accept-Language` header](https://developer.mozilla.org/docs/Web/HTTP/Headers/Accept-Language) is set by the browser and controlled by the user's language preferences in browser settings. In browser settings, a user sets one or more preferred languages in order of preference. The order of preference is used by the browser to set quality values (`q`, 0-1) for each language in the header. The following example specifies United States English, English, and Costa Rican Spanish with a preference for United States English or English:
@@ -371,6 +373,18 @@ When the culture is Costa Rican Spanish (`es-CR`), the rendered component uses d
## Statically set the client-side culture
+:::moniker range=">= aspnetcore-8.0"
+
+*This section applies to Blazor WebAssembly apps and Blazor Web App components that adopt the Interactive WebAssembly render mode.*
+
+:::moniker-end
+
+:::moniker range="< aspnetcore-8.0"
+
+*This section applies to Blazor WebAssembly apps.*
+
+:::moniker-end
+
:::moniker range=">= aspnetcore-5.0"
Set the `BlazorWebAssemblyLoadAllGlobalizationData` property to `true` in the app's project file (`.csproj`):
@@ -391,7 +405,7 @@ The Intermediate Language (IL) Linker configuration for client-side rendering st
The app's culture can be set in JavaScript when Blazor starts with the `applicationCulture` Blazor start option. The following example configures the app to launch using the United States English (`en-US`) culture.
-Prevent Blazor autostart by adding `autostart="false"` to [Blazor's `
@@ -399,7 +413,7 @@ Prevent Blazor autostart by adding `autostart="false"` to [Blazor's `