ASP.NET Core 2.1 no HTTP/HTTPS redirection in App Engine

Somehow I got this to work after creating my own middleware that look for "X-Forwarded-Proto" header according to this hint on Microsoft and App Engine documentation.

Microsoft: Forwarded Headers Middleware must be enabled for an app to process forwarded headers with UseForwardedHeaders.

App Engine: SSL connections are terminated at the load balancer. Traffic from the load balancer is sent to the instance over an encrypted channel, and then forwarded to the application server over HTTP. The X-Forwarded-Proto header lets you understand if the origin request was HTTP or HTTPs.

Microsoft requires the Middleware to be activated first before the app start to process the forwarded headers

So configure the middleware options in ConfigureServices method

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

and use it in Configure method before anything else

app.UseForwardedHeaders();

Then writing the custom middleware that reads the forwarded headers and redirect to HTTPS including queries.

In Configure method

app.Use(async (context, next) =>
{
    if (context.Request.IsHttps || context.Request.Headers["X-Forwarded-Proto"] == Uri.UriSchemeHttps)
    {
        await next();
    }
    else
    {
        string queryString = context.Request.QueryString.HasValue ? context.Request.QueryString.Value : string.Empty;
        var https = "https://" + context.Request.Host + context.Request.Path + queryString;
        context.Response.Redirect(https);
    }
});

In the end the Configure method looks like this

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    app.UseForwardedHeaders();
    app.Use(async (context, next) =>
    {
        if (context.Request.IsHttps || context.Request.Headers["X-Forwarded-Proto"] == Uri.UriSchemeHttps)
        {
            await next();
        }
        else
        {
            string queryString = context.Request.QueryString.HasValue ? context.Request.QueryString.Value : string.Empty;
            var https = "https://" + context.Request.Host + context.Request.Path + queryString;
            context.Response.Redirect(https);
        }
    });

    if (env.IsDevelopment())
    {
        // code removed for clarity
    }
    else
    {
        // code removed for clarity
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    // code removed for clarity
    app.UseMvc();
}

Now navigating to example.com redirect me directly https://www.example.com


This is what worked for me (the code is located in Startup.cs):

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<ForwardedHeadersOptions>(options =>
    {
        options.ForwardedHeaders = 
            ForwardedHeaders.XForwardedFor | 
            ForwardedHeaders.XForwardedProto;

        options.KnownNetworks.Clear();
        options.KnownProxies.Clear();
    });

    services.AddHttpsRedirection(opt => opt.HttpsPort = 443);

    // code removed for clarity
}


public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        // code removed for clarity
    }
    else
    {
        // code removed for clarity
        app.UseHsts();
    }

    app.UseForwardedHeaders();
    app.UseHttpsRedirection();
    
    // code removed for clarity
}