ASP.NET Core and Non-standard Reverse Proxy Headers

While working on a project using ASP Core on OpenShift one issue that arose was the use of a reverse proxy using non-standard headers.

A reverse proxy is a server that sits in front of web servers and forwards the requests onto those web servers. They are often used for load balancing or protecting from DDOS attacks.

Now there tends to be some standard headers that are used so ASP.Net core has provided some middleware that you can use

https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-3.1

However our reverse proxy doesn’t use the standard headers. Instead the the request headers we received were,

X-Forwarded-Host

X-Forwarded-Port

X-Forwarded-Prefix

The solution, write your own middleware.

Firstly we have to write the class that will handle the work. It must implement IMiddleware and it will look for these headers and modify the Request class accordingly.

     public class ProxyHeaderMiddleware : IMiddleware
    {
        private const string XForwardedHost = "X-Forwarded-Host";
        private const string XForwardedPort = "X-Forwarded-Port";
        private const string XForwardedPrefix = "X-Forwarded-Prefix";

        public async Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
            //Handles X-Forwarded-Port
            if (HasHeader(context, XForwardedHost, out var host))
            {

                if (HasHeader(context, XForwardedPort, out var port))
                {

                    context.Request.Host = new HostString(host, int.Parse(port));
                }
                else
                {
                    context.Request.Host = new HostString(host);
                }
            }

            //Handles X-Forwarded-Prefix
            if (HasHeader(context, XForwardedPrefix, out var basePath))
            {
                context.Request.PathBase = PathString.FromUriComponent(basePath);
            }

            await next(context);
        }


        private static bool HasHeader(HttpContext ctx, string header, out string value)
        {
            var key = ctx.Request.Headers.Keys.FirstOrDefault(x => x.Equals(header, StringComparison.OrdinalIgnoreCase));

            if (key != null && ctx.Request.Headers.TryGetValue(key, out var v1) && !StringValues.IsNullOrEmpty(v1))
            {
                value = v1;
                return true;
            }
            value = string.Empty;
            return false;
        }
    }
Code language: C# (cs)

Once we have the middleware we need to enable it. To do this we can simply use the following line in our Startup.cs file

app.UseMiddleware<ProxyHeaderMiddleware>();Code language: C# (cs)


However it is probably better to use an Extension method to make it more readable.

public static class ProxyHeaderExtensions
    {
        public static IApplicationBuilder UseProxyHeaders(this IApplicationBuilder app)
        {
            return app.UseMiddleware<ProxyHeaderMiddleware>();
        }

    }Code language: PHP (php)

Now we can use the Extension method in our Startup.cs file.

app.UseProxyHeaders();Code language: CSS (css)