diff --git a/EssentialCSharp.Web/Program.cs b/EssentialCSharp.Web/Program.cs index 48094a92..5d96e99f 100644 --- a/EssentialCSharp.Web/Program.cs +++ b/EssentialCSharp.Web/Program.cs @@ -430,6 +430,17 @@ await context.HttpContext.Response.WriteAsync( // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { + SiteSettings siteSettings = app.Services.GetRequiredService>().Value; + if (!Uri.TryCreate(siteSettings.BaseUrl, UriKind.Absolute, out Uri? configuredBaseUri)) + { + throw new InvalidOperationException($"Invalid {SiteSettings.SectionName}:{nameof(SiteSettings.BaseUrl)} value: '{siteSettings.BaseUrl}'."); + } + string apexHost = configuredBaseUri.Host.StartsWith("www.", StringComparison.OrdinalIgnoreCase) + ? configuredBaseUri.Host[4..] + : configuredBaseUri.Host; + string wwwHost = $"www.{apexHost}"; + string redirectAuthority = new UriBuilder(configuredBaseUri) { Host = apexHost }.Uri.GetLeftPart(UriPartial.Authority); + app.UseExceptionHandler(exceptionApp => { exceptionApp.Run(async context => @@ -493,6 +504,19 @@ await McpJsonRpcResponseWriter.WriteErrorAsync( app.UseSecurityHeadersMiddleware(new SecurityHeadersBuilder() .AddDefaultSecurePolicy() .AddContentSecurityPolicy(csp)); + + // Redirect configured www host to configured apex host (permanent 301). + // Must be after UseForwardedHeaders so the Host header reflects the real hostname. + app.Use(async (context, next) => + { + if (string.Equals(context.Request.Host.Host, wwwHost, StringComparison.OrdinalIgnoreCase)) + { + string redirectUrl = $"{redirectAuthority}{context.Request.PathBase}{context.Request.Path}{context.Request.QueryString}"; + context.Response.Redirect(redirectUrl, permanent: true); + return; + } + await next(context); + }); } else {