With the ASP.NET Core 2.1 release, we included UseHsts
and UseHttpRedirection
by default. These methods put a site into an infinite loop if deployed to an Azure Linux App Service, Azure Linux virtual machine (VM), or behind any other reverse proxy besides IIS. TLS is terminated by the reverse proxy, and Kestrel isn’t made aware of the correct request scheme.
OAuth and OIDC also fail in this configuration because they generate incorrect redirects. Calls to UseIISIntegration
add and configure forwarded headers middleware when running behind IIS, but there’s no matching automatic configuration for Linux (Apache or Nginx integration). The fix for this issue is discussed in more detail in the doc article Forward the scheme for Linux and non-IIS reverse proxies.
Configuration-only Wire-up in Preview 6
With the updates in .NET Core 3 preview 6, you no longer need to call the middleware explicitly, as the host logic has been pre-wired to enable the Forwarded Headers Middleware by default as long as the ASPNETCORE_FORWARDEDHEADERS_ENABLED
environment variable has been set to true
. Turning on the Forwarded Headers Middleware is as simple as setting the ASPNETCORE_FORWARDEDHEADERS_ENABLED
setting in the Azure Portal’s configuration blade for any App Service running on Linux or in a container.
Once this setting is set to true, the middleware starts working, and features dependent on Request.IsHttps
resulting to true
begin to function as expected.
Resolving the issue with ASP.NET Core 2.x Apps Today
If you’re currently building an ASP.NET Core 2.x app and want to run it on App Service for Linux now, there’s a workaround that will be future-proof when the updates come out for 3.0.
To forward the scheme from the proxy in non-IIS scenarios, add and configure Forwarded Headers Middleware. In Startup.cs
, use the following code:
// using Microsoft.AspNetCore.HttpOverrides;
public void ConfigureServices(IServiceCollection services)
{
if (string.Equals(
Environment.GetEnvironmentVariable("ASPNETCORE_FORWARDEDHEADERS_ENABLED"),
"true", StringComparison.OrdinalIgnoreCase))
{
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor |
ForwardedHeaders.XForwardedProto;
// Only loopback proxies are allowed by default.
// Clear that restriction because forwarders are enabled by explicit
// configuration.
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
}
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseForwardedHeaders();
}
If you enable your ASP.NET Core 2.x apps with this workaround today, when you’re ready to upgrade to 3.0, you’ll already have the right configuration setting in place.
Base Image Update
The base images used by the App Service team to streamline the creation of ASP.NET Core apps will soon be updated so that the ASPNETCORE_FORWARDEDHEADERS_ENABLED
environment variable will be set to true
. Once they’re updated, you won’t even need to explicitly set the environment variable; it’ll be enabled by default.
Try it Out
If you’re new to building ASP.NET Core apps using containers, the App Service options for Linux and Container-based hosting offer a great place to get started. The docs are loaded with guidance and examples, from how to Run a .NET Core app in App Service on Linux to accessing a SQL Server Database from an ASP.NET Core app running in App Service Linux.
Brady, why ForwardedHeaders.XForwardedFor option use X-Client-IP header value instead of X-Forwarded-For? I use CloudFlare and it returns two headers among others:
X-Client-IP : The proxy IP
X-Forwarded-For : The actual client IP
When I enable ASPNETCORE_FORWARDEDHEADERS_ENABLED=true the ASP.NET Core 3.1 reads IP from X-Client-IP and not from X-Forwarded-For header.
Is it possible to change the header name from where to read IP?
Just in case you need that for .NET Core 2.x – https://github.com/alefranz/HeaderPropagation
I’m guessing the code sample is a little bit off:
Is the type of forwardedheadersoptions really all lowercase?
You are correct. I’ll fix that, stat. Thanks!
Fixed. Again, thanks!
spent quite some time figured that out when Deploying sites behind traffic. it’s the exact solution that fixed the infinite loop for identify server