In this post I describe how to configure the urls your application binds to when using the Kestrel or WebListener HTTP servers that come with ASP.NET Core. I'll also cover how to set the url when you are developing locally with Visual Studio using IIS Express, and how this relates to Kestrel and WebListener.

Background

In ASP.NET Core the hosting model has completely changed from ASP.NET 4.x. Previously your application was inextricably bound to IIS and System.Web, but in ASP.NET Core, your application is essentially just a console app. You then create and configure your own lightweight HTTP server within your application itself. This provides a larger range of hosting options than just hosting in IIS - in particular self-hosting in your own process.

ASP.NET Core comes with two HTTP servers which you can plug straight in out of the box. If you have been following the development of ASP.ENT Core at all, you will no doubt have heard of Kestrel, the new high performance, cross-platform web server built specifically for ASP.NET Core.

The other server is WebListener. Kestrel get's all the attention, and probably rightly so given it's performance and cross-platform credentials, but web listener is actually more fully featured, in particular regarding platform specific features such as Windows-Authentication.

While you can directly self-host your ASP.NET Core applications using Kestrel or WebListener, that's generally not the recommended approach when you come to deploy your application. According to the documentation:

If you intend to deploy your application on a Windows server, you should run IIS as a reverse proxy server that manages and proxies requests to Kestrel. If deploying on Linux, you should run a comparable reverse proxy server such as Apache or Nginx to proxy requests to Kestrel.

For self-hosting scenarios, such as running in Service Fabric, we recommend using Kestrel without IIS. However, if you require Windows Authentication in a self-hosting scenario, you should choose WebListener.

Using a reverse-proxy generally brings a whole raft of advantages. IIS can for example handle restarting your app if it crashes, it can manage the SSL layer and certificates for you, it can filter requests, as well as handle hosting multiple applications on the same server.

Configuring Urls in Kestrel and WebListener

Now we have some background on where Kestrel and WebListener fit, we'll dive into the mechanics of configuring the servers to listen at the correct urls. To be clear, we are talking about setting the url to which our application is bound e.g. http://localhost:5000, or http://myfancydomain:54321, which you would navigate to in your browser to view your app.

There is an excellent post from Ben Foster which shows the most common ways to configure Urls when you are using Kestrel as the web server. It's worth noting that all these techniques apply to WebListener too.

These methods assume you are working with an ASP.NET Core application created using one of the standard templates. Once created, there will be a file Program.cs which contains the static void main() entry point for your application:

public class Program  
{
    public static void Main(string[] args)
    {
        var host = new WebHostBuilder()
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseKestrel()
            .UseIISIntegration()
            .UseStartup<Startup>()
            .Build();

        host.Run();
    }
}

It is the WebHostBuilder class which configures your application and HTTP server (in the example, Kestrel), and starts your app listening for requests with host.Run().

UseUrls()

The first, and easiest, option to specify the binding URLs is to hard code them into the WebHostBuilder using AddUrls():

var host = new WebHostBuilder()  
    .UseKestrel()
    .UseContentRoot(Directory.GetCurrentDirectory())
    .UseUrls("http://localhost:5100", "http://localhost:5101", "http://*:5102")
    .UseIISIntegration()
    .UseStartup<Startup>()
    .Build();

Simply adding this one line will allow you to call your application at any of the provided urls, and even at the wildcard host. However, hard-coding the urls never feels like a particularly clean or extensible solution. Luckily, you can also load the urls from an external configuration file, from environment variables, from command line arguments, or any source supported by the Configuration system.

External file - hosting.json

To load from a file, create hosting.json, in the root of your project, and set the server.urls key as appropriate, separating each url with a semicolon. You can actually use any name now, the name hosting.json is no longer assumed, but it's probably best to continue to use it by convention.

{
  "server.urls": "http://localhost:5100;http://localhost:5101;http://*:5102"
}

Update your WebHostBuilder to load hosting.json as part of the initial configuration. It's important to set the base path so that the ConfigurationBuilder knows where to look for your hosting.json file.

var config = new ConfigurationBuilder()  
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile("hosting.json", optional: true)
    .Build();

var host = new WebHostBuilder()  
    .UseConfiguration(config)
    .UseContentRoot(Directory.GetCurrentDirectory())
    .UseKestrel()
    .UseIISIntegration()
    .UseStartup<Startup>()
    .Build();

Note that the ConfigurationBuilder we use here is distinct from the ConfigurationBuilder typically used to read appsettings.json etc as part of your Startup.cs configuration. It is an instance of the same class, but the WebHostBuilder and app configuration are built separately - values from hosting.json will not pollute your appsettings.json configuration.

Command line arguments

As mentioned previously, and as shown in the previous snippet, you can configure your WebHostBuilder using any mechanism available to the ConfigurationBuilder. If you prefer to configure your application using command line arguments, add the Microsoft.Extensions.Configuration.CommandLine package to your project.json and update your ConfigurationBuilder to the following:

var config = new ConfigurationBuilder()  
    .AddCommandLine(args)
    .Build();

You can then specify the urls to use at runtime, again passing in the urls separated by semicolons:

> dotnet run --server.urls "http://localhost:5100;http://localhost:5101;http://*:5102"

Environment Variables

There are a couple of subtleties to using environment variables to configure your WebHostBuilder. The first approach is to just set your own environment variables and load them as usual with the ConfigurationBuilder. For example, using PowerShell you could set the variable "MYVALUES_SERVER.URLS" using:

[Environment]::SetEnvironmentVariable("MYVALUES_SERVER.URLS", "http://localhost:5100")

which can be loaded in our configuration builder using the prefix "MYVALUES_", allowing us to again set the urls at runtime:

var config = new ConfigurationBuilder()  
    .AddEnvironmentVariables("MYVALUES_")
    .Build();

ASPNETCORE_URLS

The other option to be aware of is the special environment variable, "ASPNETCORE_URLS". If this is set, it will overwrite any other values that have been set by UseConfiguration, whether from hosting.json or command line arguments etc. The only way (that I found) to overwrite this value is with an explicit UseUrls() call.

Using the ConfigurationBuilder approach to configuring your server gives the most flexibility at runtime for specifying your urls, so I would definitely encourage you to use this approach any time you find the need to specify your urls. However, you may well find the need to configure your Kestrel/WebListener urls at all surprisingly rare…

Configuring IIS Express and Visual Studio

In the previous section I demonstrated how to configure the urls used by your ASP.NET Core hosting server, whether Kestrel or WebListener. While you might directly expose those self-hosted servers in some cases (the docs cite Service Fabric as a possible use case), in most cases you will be reverse-proxied behind IIS on Windows, or Apache/Nginix on Linux. This means that the urls you have configured will not be the actual urls exposed to the outside world.

You can see this effect for yourself if you are developing using Visual Studio and IIS. When you create a new ASP.NET Core project in Visual studio 2015 (Update 3), a launchSettings.json file is created inside the Properties folder of your project. This file contains settings for when you host your application in IIS:

{
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:55862/",
      "sslPort": 0
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "ExampleWebApplication": {
      "commandName": "Project",
      "launchBrowser": true,
      "launchUrl": "http://localhost:5000",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

This file contains two sections, iisSettings and profiles. When running in visual studio, profiles provides the hooks required to launch and debug your application using F5. In this case we have two profiles; IIS Express, which fairly obviously runs the application using IIS Express; and ExampleWebApplication, the name of the web project, which runs the application using dotnet run.

the F5 menu for running the application

These two profiles run your application in two distinct ways. The project profile, ExampleWebApplication, runs your application as though you had run the application directly using dotnet run from the command line - it even opens the console window that is running the application. In contrast, IIS Express hosts your application, acting as a reverse-proxy in the same wasy as IIS would in production.

If you were following along with updating your urls, using the methods described in the previous section and running using F5, you may have found that things weren't running as smoothly as expected.

When running using IIS Express or IIS, the urls you configure as part of Kestrel/WebListener are essentially unused - it is the url configured in IIS that is publicly exposed and which you can navigate to in your browser. Therefore when developing locally with IIS Express, you must update the iisSettings:applicationUrl key in launchSettings.json to change the url to which you are bound. You can also update the url in the Debug tab of Properties (Right click your project -> Properties -> Debug).

How to change the IIS express url using properties tab

In contrast, when you run the project profile, the urls you configure using the previous section are directly exposed. However, by default the profile opens a browser window - which requires a url - so a default url of http://localhost:5000 is specified in the launchSettings setting. If you are running directly on Kestrel/Weblistener and are customising your urls don't forget to update this setting to load the correct url, or set "launchBrowser": false!

Summary

This post gave a brief summary of the changes to the server hosting model in ASP.NET Core. It described the various ways in which you can specify the binding urls when self-hosting your application using the Kestrel or WebListener servers. Finally it described things to look out for related to changing your urls when developing locally using Visual Studio and IIS Express.