Serving HTTPS in both Kestrel and IIS Integration modes

Home / Serving HTTPS in both Kestrel and IIS Integration modes

I’ve previously blogged about dealing with SSL in a .NET Core application. Well, I wanted to expand upon this a bit when debugging in Visual Studio 2017. In the case of working with a development environment, this needs to tie into the launch profiles. Without handling this properly, IISExpress integration is broken which becomes an annoyance.


Based on the launch profile chosen, we may or may not want Kestrel to serve HTTPS content. We need to detect, then, if we are using an externally hosted (IIS/Nginx) proxy, within our IDE hosting environment with IISExpres proxy, or simply using Kestrel. The latter condition is the one in which we want to attach an SSL cert to Kestrel and tell it to use it.

First, I added environment variables to each of my launch profiles. I use these to easily determine which launch profile was used in my IDE. The environment variable I added is called “LAUNCH_PROFILE.”

{
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:8111/",
      "sslPort": 44111
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "launchUrl": "https://localhost:44111/",
      "environmentVariables": {
        "LAUNCH_PROFILE": "IISExpress",
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "MyProject.Web": {
      "commandName": "Project",
      "launchBrowser": true,
      "launchUrl": "https://localhost:44111",
      "environmentVariables": {
        "LAUNCH_PROFILE": "Kestrel",
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

The nice thing about injecting environment variables with the launch profiles is that they will only exist when debugging within Visual Studio. Secondly, there is the “hosting.json” that configures which ports on which Kestrel will listen. Incidentally, this file does not get deployed by “dotnet publish” which makes it a good candidate to use as one of our conditions. If it doesn’t exist, then we have some assurance that we are not within our local IDE any more.

{
  "urls": "http://*:5000;https://localhost:44111"
}

Tying it all together, our Program.cs will use these elements/conditions to determine the (3) cases previously mentioned:

  • Are we running in a web proxy environment?
  • Are we running in the debugger with IISExpress proxy integration?
  • Are we running solely with Kestrel in the debugger?

The Program.cs then looks like this:

public class Program
{
    public static void Main(string[] args)
    {
        string env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
        string launch = Environment.GetEnvironmentVariable("LAUNCH_PROFILE");

Grab the environment variables and then perform our comparisons If all conditions are true, we are running Kestrel and want it to use HTTPS..

        IWebHost host = null;

        if (string.Equals(env, "Development", StringComparison.OrdinalIgnoreCase) &&
            string.Equals(launch, "Kestrel", StringComparison.OrdinalIgnoreCase) &&
            File.Exists($"{Directory.GetCurrentDirectory()}/{@"Properties/hosting.json"}"))
        {
            var config = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile(@"Properties/hosting.json", optional: false, reloadOnChange: true)
            .Build();

This section is parsing out the HTTPS port so that we can launch the browser.

            var regEx = new Regex(@"((http[s]?):\/\/)([\w\d\.]*)(?:\:\d+)");
            var rootUrl = regEx.Match(config["urls"]).Value;

            host = new WebHostBuilder()
                .UseConfiguration(config)
                .UseKestrel(options => options.UseHttps(new X509Certificate2("testCert.pfx", "testPassword")))
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseStartup<Startup>()
                .UseIISIntegration()
                .Build();

            OpenBrowser(rootUrl);
        }

Our else condition is to launch Kestrel normally without HTTPS/SSL integration. This will allow it to behave properly with whatever proxy we put in front of it.

        else
        {
            host = new WebHostBuilder()
                .UseKestrel()
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseStartup<Startup>()
                .UseIISIntegration()
                .Build();
        }
        host.Run();
    }

Finally, this is a helper method I have for launching the OS’s browser. It’s helpful if using “dotnet run.”

    public static void OpenBrowser(string url)
    {
        try
        {
            Process.Start(url);
        }
        catch
        {
            // hack because of this: https://github.com/dotnet/corefx/issues/10361
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                url = url.Replace("&", "^&");
                Process.Start(new ProcessStartInfo("cmd", $"/c start {url}") { CreateNoWindow = true });
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            {
                Process.Start("xdg-open", url);
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
            {
                Process.Start("open", url);
            }
            else
            {
                throw;
            }
        }
    }
}

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.