Header Forwarding for ASP.NET Core Applications

Kestrel is the default web server to run ASP.NET Core application. It provides basic web server functionality while ensures the application is cross-platform. However, such application is more often than not running behind a more full-featured reverse proxy such as NGINX. Using NGINX as reverse proxy helps handling HTTP requests and adding a layer of protection (with secured HTTP or HTTPS) by working as a gateway to the server. However, such setup omits certain HTTP information when passing the requests to our application. This issue can be solved by instructing NGXINX to set headers explicitly which will be processed by the application. The official documentation shows how this can be done by using the proxy_set_header directive:

server {
  listen        80;
  server_name   example.com *.example.com;
  location / {
    ...
    proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header   X-Forwarded-Proto $scheme;
  }
}

And using the ForwardHeader middleware at the top of the pipeline before any other middleware:

app.UseForwardedHeaders();

This ensures the HTTP context such as the scheme is passed over to our application.

However, the configuration can be different depending on different hosting strategy that we use for our application. Here we look at three common strategies:

  1. Hosting the application locally (localhost)
  2. Hosting the application behind NGINX or any reverse proxy
  3. Hosting the application inside docker container behind NGINX

We will look at each of the strategies below.

Hosting the Application Locally

Hosting application locally

It is not necessary to use the header forwarding since there is no proxy used. The requests go to the application through Kestrel directly.

Hosting the Application Behind NGINX

Hosting the application behind a reverse proxy

The reverse proxy (such as NGINX) has to set the above mentioned headers explicitly so that the application can get certain information such as Remote IP address and Scheme from the interface between NGINX and internet (client).

Hosting the Application Inside Docker Container Behind NGINX

Hosting the application inside Docker container behind NGINX

Thing is a little tricky here if we run the application inside a Docker container because there is another layer of network between NGIXN and Docker container. If we use the same configuration as what we use for the setup without Docker, the header forwarding is working as if it is disabled.

This is because header forwarding uses loopback IP address as the default known networks when obtaining the headers. The loopback is the localhost or 127.0.0.1. However, the IP address inside a Docker container is ::ffff:172.17.0.2 which is not known by the application, hence the headers are not accepted or processed. To solve this, we have to clear the known networks in the configuration:

services.Configure<ForwardedHeadersOptions>(options =>
  {
    options.ForwardedHeaders =
      ForwardedHeaders.XForwardedFor | 
      ForwardedHeaders.XForwardedProto;
    
    options.ForwardLimit = 2;
    options.KnownNetworks.Clear();
    options.KnownProxies.Clear();
  });

This ensure the default networks (and proxies) are cleared so the application will then accept headers from any IP address. The comparison below shows the differences with and without clearing the known network values:

PropertiesWithout clearing known networksWith clearing known networks
Remote IP address::ffff:172.17.0.1<client IP address, hidden>
Remote port number:485000
Local IP address::ffff:172.17.0.1::ffff:172.17.0.1
Local port number8080
Schemehttphttps
Hostcoffee-shop-talk-stg.tengweisong.comcoffee-shop-talk-stg.tengweisong.com
Differences between forwarding header with and without clearing the known network values

As we can see, just by clearing the known networks, headers are passed successfully to the application. The remote IP address represents the IP address the HTTP requests originates from, and the scheme picks up the https correctly because the connection between client and NGINX is protected by SSL certificate while the rest of the connection is http only (between NGINX and Docker container as well as between Docker container and application).

Icons made by Smashicons from www.flaticon.com

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s