Deploying an ASP .Net Core app on Heroku with SSL

05 February 2020 — Written by Andrew Timney
#dotnet#heroku#devops

Heroku is great and makes deploying apps very easy but it does not natively provide a way to deploy .net apps despite supporting nearly all other popular languages like Java, Node, GO, Ruby etc. Fortunately they allow you to use open source buildpacks to build your application and there are a ton out there that you can find on their buildpacks page making it very easy to find one that suits. To deploy our asp .net core application we are going to use this provided by github user jincod 👏.

There are a few ways to add a buildpack, on the settings page for your app you can add a url to a buildpack, which is what I done to add the buildpack above, notice the tag on the end of the url for .net core v3.1, or you can select one supported by heroku.

Heroku buildpack settings

You can also add a buildpack using the heroku cli

heroku buildpacks:set https://github.com/jincod/dotnetcore-buildpack#v3.1 -a myapp

SSL

Adding a cert and handling x-forwarded-for header.

You can add an SSL cert from the settings page and paid dynos have automated certificate management which means they'll automatically renew every month.

Your app in heroku sits behind a proxy which means you can't simply use the built int UseHttpsRedirection method, we need to check the X-Forwarded-Proto header which contains the original requested protocal.

Here is a simple middleware that redirects if the protcol is not "https", found on this blog post.

public class ReverseProxyHttpsEnforcer {
    private const string ForwardedProtoHeader = "X-Forwarded-Proto";
    private readonly RequestDelegate _next;

    public ReverseProxyHttpsEnforcer(RequestDelegate next) {
        _next = next;
    }

    public async Task Invoke(HttpContext ctx) {
        var h = ctx.Request.Headers;
        if (h[ForwardedProtoHeader] == string.Empty || h[ForwardedProtoHeader] == "https") {
            await _next(ctx);
        } else if (h[ForwardedProtoHeader] != "https") {
            var withHttps = $"https://{ctx.Request.Host}{ctx.Request.Path}{ctx.Request.QueryString}";
            ctx.Response.Redirect(withHttps);
        }
    }
}

public static class ReverseProxyHttpsEnforcerExtensions {
    public static IApplicationBuilder UseReverseProxyHttpsEnforcer(this IApplicationBuilder builder) {
        return builder.UseMiddleware<ReverseProxyHttpsEnforcer>();
    }
}

This works great, thanks to the internet.

Links