diff --git a/EssentialCSharp.Web/Extensions/IServiceCollectionExtensions.cs b/EssentialCSharp.Web/Extensions/IServiceCollectionExtensions.cs index fecb7c4e..1c3806ca 100644 --- a/EssentialCSharp.Web/Extensions/IServiceCollectionExtensions.cs +++ b/EssentialCSharp.Web/Extensions/IServiceCollectionExtensions.cs @@ -18,6 +18,12 @@ public static void AddCaptchaService(this IServiceCollection services, IConfigur public static void AddTrustedForwardedHeaders(this IServiceCollection services, IConfiguration configuration, IHostEnvironment environment) { + // When ASPNETCORE_FORWARDEDHEADERS_ENABLED=true (recommended for Azure Container Apps), + // ASP.NET Core's built-in startup filter handles ForwardedHeaders with all proxies trusted. + // Skip manual configuration to avoid a conflicting no-trusted-proxies throw on startup. + if (string.Equals(configuration["ASPNETCORE_FORWARDEDHEADERS_ENABLED"], "true", StringComparison.OrdinalIgnoreCase)) + return; + services.Configure(options => { options.ForwardedHeaders = @@ -37,7 +43,8 @@ public static void AddTrustedForwardedHeaders(this IServiceCollection services, { throw new InvalidOperationException( "Forwarded headers are enabled but no trusted proxies are configured. " + - "Set ForwardedHeaders:TrustedProxyCidrs or ForwardedHeaders:TrustedProxies."); + "Set ForwardedHeaders:TrustedProxyCidrs or ForwardedHeaders:TrustedProxies, " + + "or set ASPNETCORE_FORWARDEDHEADERS_ENABLED=true for platform-managed proxies (e.g. Azure Container Apps)."); } return; } diff --git a/EssentialCSharp.Web/Program.cs b/EssentialCSharp.Web/Program.cs index a16ca957..cfc2c488 100644 --- a/EssentialCSharp.Web/Program.cs +++ b/EssentialCSharp.Web/Program.cs @@ -481,7 +481,12 @@ await McpJsonRpcResponseWriter.WriteErrorAsync( } }); }); - app.UseForwardedHeaders(); + // Skip manual UseForwardedHeaders when ASPNETCORE_FORWARDEDHEADERS_ENABLED=true; + // the built-in startup filter already called it before this pipeline runs. + if (!string.Equals(app.Configuration["ASPNETCORE_FORWARDEDHEADERS_ENABLED"], "true", StringComparison.OrdinalIgnoreCase)) + { + app.UseForwardedHeaders(); + } // Build dynamic CSP — TryDotNet origin comes from runtime config string? tryDotNetOrigin = app.Configuration["TryDotNet:Origin"]; @@ -532,7 +537,10 @@ await McpJsonRpcResponseWriter.WriteErrorAsync( else { app.UseDeveloperExceptionPage(); - app.UseForwardedHeaders(); + if (!string.Equals(app.Configuration["ASPNETCORE_FORWARDEDHEADERS_ENABLED"], "true", StringComparison.OrdinalIgnoreCase)) + { + app.UseForwardedHeaders(); + } } app.MapHealthChecks("/health").DisableRateLimiting();