Technology Enthusiast, Software Engineer, Architect & Craftsman, DevOps Human & All Round Disney Lover

ASP.NET Core Reverse Proxy Support With HAProxy

ASP.NET Core Reverse Proxy Support With HAProxy

Dan Horrocks-Burgess
Dan Horrocks-Burgess

I’ve recently worked on many APIs that were developed using .NET Core, ASP.NET Core 2.2 to be exact. A Microsoft Azure hosted Kubernetes cluster (AKS) is used to host the web app using HAProxy as an Ingress Controller.

Traffic was set to terminate at the load balancer (in this case HAProxy is my ingress controller on Kubernetes) rather than using SSL Passthrough. This meant traffic reached the web application over port 80, rather than origin port of 443.

The Problem

HAProxy is reverse proxying the request from port 443 to 80 – this means the API thought all traffic was being served over port 80 (non-encrypted). This meant the web application was incorrectly generating URLs.

A good example of the problem was the login Redirect URL generated by the OpenID middleware. The middleware automatically generated the return redirect URL to use HTTP rather than HTTPS. (I assume more problems would have cropped up if I used helpers to create fully qualified URLs)

A Simple Solution

Thankfully, HAProxy can send the widely used X-Forwarded-Proto header. This tells the application of the original protocol before it reversed proxied the request. For example, if the original request was served over https, we can pass that header down to the application.

If you’re using the supported HAProxy ingress controller for Kubernetes this header is automatically set. Alternatively, it’s as simple as setting the following in your HAProxy config file in your frontend:

acl from-https ssl_fc
http-request set-header X-Forwarded-Proto https if from-https

Once this is done, ASP.NET core just needs to know to look at this header, add the following code to your startup file (Under Configure Services):

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