How to fix Request.Url.Scheme returning http instead of https on load balanced site

I was configuring a website behind a load balancer which offloads SSL, when I enabled the Umbraco setting of Umbraco.Core.UseHttps it would cause a infinite redirect loop.

The reason for this was pretty simple, the client would hit the load balancer with HTTPS, the load balancer would then in turn convert this into HTTP and proxy to the server internally over HTTP.
The attribute which Umbraco uses to handle the Umbraco.Core.UseHttps redirect does not check for the X-Forwarded-Proto header (I am working on a core Umbraco fix for this), due to this lack of check, even though the client is using HTTPS, Umbraco / MVC doesn't detect this.

One of the other issues I noticed once I got the redirect loop to stop, prior to the fix below, was that the Google Auth provider uses the request schema to set the callback url, and as such due to .NET thinking it was running in HTTP, the callback was HTTP and not HTTPS, a scheme the Google Auth wasn't configured to allow.
Annoyingly Google Auth only allows for the Path to be configured, and not the whole callback URL.

After several more hours of playing with rewrite rules, overriding the attribute and added routing handlers, I found a similar issue on trusty old Stack Overflow.

One of the answers provided suggested to use a HttpModule to intercept the request, and basically for MVC to see HTTPS using the ServerVariables

Below you can find the HttpModule as posted on Stack Overflow, this is more for my future reference that anything else.

HttpOverrides.cs

public sealed class HttpOverrides : IHttpModule
{
    void IHttpModule.Init(HttpApplication app)
    {
        app.BeginRequest += OnBeginRequest;
    }

    private void OnBeginRequest(object sender, EventArgs e)
    {
        HttpApplication app = (HttpApplication)sender;

        string forwardedFor = app.Context.Request.Headers["X-Forwarded-For"]?.Split(new char[] { ',' }).FirstOrDefault();
        if (forwardedFor != null)
        {
            app.Context.Request.ServerVariables["REMOTE_ADDR"] = forwardedFor;
            app.Context.Request.ServerVariables["REMOTE_HOST"] = forwardedFor;
        }

        string forwardedProto = app.Context.Request.Headers["X-Forwarded-Proto"];
        if (forwardedProto == "https")
        {
            app.Context.Request.ServerVariables["HTTPS"] = "on";
            app.Context.Request.ServerVariables["SERVER_PORT"] = "443";
            app.Context.Request.ServerVariables["SERVER_PORT_SECURE"] = "1";
        }
    }

    void IHttpModule.Dispose()
    {
    }
}

Register in Web.Config

<system.webServer>
    <modules runAllManagedModulesForAllRequests="true">
        <add name="HttpOverrides" type="Namespace.HttpOverrides" preCondition="integratedMode" />
    </modules>
</system.webServer>

Once this has been added to the site, the redirect loop should be gone, the Umbraco health check for HTTPS should pass, and Google Auth (if you are using it) should have HTTPS as the callback URL.

This fix will also work with non Umbraco websites, it should work with any .NET Framework websites.

Comments (0)