Problem


I have a web app with a web front end and API backend both deployed to Azure App Service. I reversed proxy the API using this method I described before, so that my web front end can access API without having CORS issue, and the end users won't need to access my API address directly. However, for the API code, the client IP address I got is always Azure app service's outbound IP instead of real client IP. Let's see how to fix this problem.

Solution


Just like with any reverse proxy, ASP.NET Core already has built in support for respecting "X-Forwarded-For" HTTP header. 

Typically, we will add this in DI

builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});

And use in Middleware

app.UseForwardedHeaders();

However, this is not enough on Azure. We also need to manually add all the possible proxy IP addresses to the ForwardedHeadersOptions

Get your App Service's outbound IP addresses

In my case, I call API from Web server. So, I go to the "Networking" blade in the web app's instance and copy all Outbound addresses to clipboard.

Set known proxies


I don't like hard code IP addresses in code. So I go to appsettings.json file and add a new section named "AzureKnownProxies" with the IP I copied before.

 "AzureKnownProxies": [
   "52.163.244.128",
   "13.75.34.175",
   "168.63.220.81",
   "207.46.136.220",
   "168.63.210.90",
   "23.101.15.21",
   "23.101.7.253",
   "207.46.136.152",
   "65.52.180.140",
   "23.101.13.231",
   "23.101.3.51"
 ],

Then, modify DI code for adding forwarded headers to this

var knownProxies = builder.Configuration.GetSection("AzureKnownProxies").Get<string[]>();
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
    options.ForwardLimit = null;
    options.KnownProxies.Clear();
    if (knownProxies != null)
    {
        foreach (var ip in knownProxies)
        {
            options.KnownProxies.Add(IPAddress.Parse(ip));
        }
    }
});

Now, we can successfully get real client IP address!