By default, without additional configuration in .NET 8, ASP.NET Core apps listen on the URL http://localhost:5000. In this post I show 8 different ways to change this URL. This is an update to a post I wrote three years ago called “5 ways to set the URLs for an ASP.NET Core app”. This post is similar but covers some additional ways! 😅
There are multiple ways to set the URLs that ASP.NET Core binds to on startup. I have a very old post about the options available in ASP.NET Core 1.0, and my previous post applies to .NET Core 3.x. The options available in ASP.NET Core 8 are similar, with a couple of additions:
UseUrls()
—Set the URLs by callingWebApplicationBuilder.WebHost.UseUrls()
.WebApplication.Urls
—Add the URLs to use toWebApplication.Urls
directly.- Environment variables for the URL—Set the URLs using
DOTNET_URLS
orASPNETCORE_URLS
. - Environment variables for the port—Set the ports to use using
DOTNET_HTTP_PORTS
orASPNETCORE_HTTP_PORTS
(and their HTTPS equivalents). - Command line arguments—Set the URLs with the
--urls
parameter when running from the command line. - Using appSettings.json—Set the URLs using the
urls
property - Using launchSettings.json—Set the URLs using the
applicationUrl
property. KestrelServerOptions.Listen()
—Configure addresses for Kestrel server manually in code usingListen()
or inIConfiguration
using theKestrel
property.
We'll look at each of these options in more detail below.
What URLs can you use?
In this post I describe the "URLs" you can bind to, but you can't use just any URL. There are essentially 3 classes of URLs that you can bind:
- The "loopback" hostname for IPv4 and IPv6 (e.g.
http://localhost:5000
,http://127.0.0.1:5000
, orhttp://[::1]:5000
), in the format:{scheme}://{loopbackAddress}:{port}
- A specific IP address available on your machine (e.g.
http://192.168.8.31:5005
), in the format{scheme}://{IPAddress}:{port}
- "Any" IP address for a given port (e.g.
http://*:6264
), in the format{scheme}://*:{port}
The port
in the above patterns is also optional - if you omit it, the default port for the given scheme
is used instead (port 80
for http
, port 443
for https
).
In .NET 8 you can also listen to requests on named pipes and Unix Sockets, in addition to TCP, which I'll discuss in a different post.
Which of these patterns you choose will depend on your deployment mechanism. For example, if you're hosting multiple applications on a "bare metal" machine, you may well need to set an explicit IPAddress. If you're hosting in a container, then you can generally use an "any" IP address.
Watch out for the "any" IP address format - you don't have to use
*
, you can use anything that's not an IP Address and is not the loopback address likelocalhost
. That means you can usehttp://*
,http://+
,http://mydomain
, orhttp://example.org
. All of these behave identically for configuring Kestrel, and enable listening on any IP address. Note that you should you make sure to also configure host filtering for security reasons as described in this post.
Once you know the URLs you need to listen on, you need to tell your application about them. In this post I show 8 possible ways of doing that.
UseUrls()
The first, and easiest, option to set the binding URLs is to hardcode them when configuring the WebApplicationBuilder
using UseUrls()
. The UseUrls()
method is an extension method exposed on the WebHost
property:
var builder = WebApplication.CreateBuilder(args);
// Configure the URLs 👇
builder.WebHost.UseUrls("http://localhost:5003", "https://localhost:5004");
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
The UseUrls()
method has been around since version 1.0 of ASP.NET Core, but since .NET 6 there's also a newer approach using WebApplication
directly.
WebApplication.Urls
and WebApplication.Run()
WebApplication
exposes a Urls
property for configuring which URLs the application should listen to:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Urls.Add("http://*:5003"); // 👈 Add the URL
app.Run();
Behind the scenes, the Urls
property exposes the IServerAddressFeature.Addresses
collection, so you're adding the addresses directly there:
public ICollection<string> Urls => ServerFeatures.GetRequiredFeature<IServerAddressesFeature>().Addresses;
A similar, related way to set the URL is to provide it in the call to app.Run()
. The URL you pass here is added to the IServerAddressesFeature
collection just before the application is started.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run("http://*:5003"); // 👈 Use the provided URL when running the application
Note that you can only provide a single URL when using the app.Run()
approach, so you need to use one of the other approaches if you want to set multiple URLs.
Hard-coding the URLs never feels like a particularly clean or extensible solution, so you probably won't use this approach in general. Luckily, you can also load the URLs from configuration files, from environment variables, or from command line arguments.
Environment variables for the URL
ASP.NET Core has an advanced, extensible, configuration system that can load values from multiple configuration sources. By default, the ConfigurationManager
used in .NET 8 loads values from the following locations:
- appsettings.json JSON file
- Optional environment-specific JSON file, appsettings.ENVIRONMENT.json
- User Secrets
- Environment variables
- Command-line arguments
You can add additional providers to load from other locations, but these are the ones added by default. You can use this system to load settings and customise your application, but ASP.NET Core also uses it to configure its internals.
ASP.NET Core adds all the following to the configuration system:
- All environment variables are added directly to configuration.
- Environment variables that have the prefix
DOTNET_
have the prefix removed and are added to the collection. - For ASP.NET Core apps, environment variables that have the prefix
ASPNETCORE_
have the prefix removed and are added to the collection These aren't added if you are creating a generic-host-based worker service (usingHostApplicationBuilder
).
If you don't override them manually with UseUrls()
, then ASP.NET Core will use the value of the URLS
key from the configuration system. Based on the description above you can set the URLS using any of the following environment variables (casing doesn't matter):
URLS
ASPNETCORE_URLS
DOTNET_URLS
The order above also describes the order of precedence if you set all these environment variables, with
URLS
having the highest precedence,
You can set environment variables in the usual way based on your environment. For example, using the command line:
setx ASPNETCORE_URLS "http://localhost:5001"
using powershell
$Env:ASPNETCORE_URLS="http://localhost:5001"
or using bash:
export ASPNETCORE_URLS="http://localhost:5001;https://localhost:5002"
As you can see above, you can also pass multiple addresses to listen on (using HTTP or HTTPS) by separating them with a semicolon.
Environment variables for the ports
The ASPNETCORE_URLS
and similar environment variables can be used to set the URLs using the formats shown at the start of this post, for example:
ASPNETCORE_URLS="http://localhost:5001"
—listen to port 5001 on the loopback (localhost
address).ASPNETCORE_URLS="http://192.168.8.31:5001"
—listen to port 5001 on the specified IP address.ASPNETCORE_URLS="http://*:5001"
—listen to port 5001 on any IP address
One thing that's always bugged me a little is the name of this variable. It implies (to me) that you can use things like http://example.com:5000
and your app will listen on that DNS name. That's not the case. The above URL is treated exactly the same as http://*:5000
, i.e. it configures the app to listen to port 5000
on any IP address.
.NET 8 added new configuration keys to be more explicit about this. Instead of specifying "URLs" you specify HTTP_PORTS
and HTTPS_PORTS
, and these are used to bind any IP address. You can specify multiple ports using a semicolon separator. For example, if you set the following environment variables:
ASPNETCORE_HTTP_PORTS=5001;5002
ASPNETCORE_HTTPS_PORTS=5003
These are expanded to the following URLs:
http://*:5001
http://*:5002
https://*:5003
Just like other configuration keys, you can use ASPNETCORE_
, DOTNET_
, or "no prefix" to specify the values:
HTTP_PORTS
(andHTTPS_PORTS
)ASPNETCORE_HTTP_PORTS
(andASPNETCORE_HTTPS_PORTS
)DOTNET_HTTP_PORTS
(orDOTNET_HTTPS_PORTS
)
Note that if you specify one of these and ASPNETCORE_URLS
(for example) then the ASPNETCORE_URLS
value takes precedence, and a warning is logged to the output:
warn: Microsoft.AspNetCore.Hosting.Diagnostics[15]
Overriding HTTP_PORTS '5002' and HTTPS_PORTS ''. Binding to values defined by URLS instead 'http://*:5003'.
In .NET 8, the
ASPNETCORE_HTTP_PORTS
variable is set to8080
by default in the .NET 8 docker images (whereasASPNETCORE_URLS
was set in previous version docker images). You can override this default value by settingASPNETCORE_HTTP_PORTS
orASPNETCORE_URLS
(among other ways).
Environment variables are just one way to add values to configuration; command line arguments are another.
Command line arguments
Another way to add values to ASP.NET Core's configuration system is to use the command line. Command line arguments override the value of the environment variables if they're set. Simply pass the "un-prefixed" environment variable as an argument when running the application, and prefix with --
. For example:
dotnet run -- --urls "http://localhost:5100"
As before, you can pass multiple URLs to listen on by separating them with a semicolon:
dotnet run -- --urls "http://*:5100;http://localhost:5101"
This also works with the dotnet
runtime version where you pass the path of the compiled dll to dotnet
instead of using dotnet run
:
dotnet ./WebApplication1.dll --urls "http://*:5100;http://localhost:5101"
You can also use --http_ports
and --https_ports
:
dotnet run -- --http_ports "5100;5101" --https_ports "5003;5004"
Note that the extra
--
in thedotnet run
command is not a typo; it's to ensure the arguments are definitely interpreted as arguments for configuration, not as arguments to therun
command itself.
Environment variables and command line arguments are common ways to add values to ASP.NET Core configuration, but you can use any provider. One of the most frequently used ways to add values to configuration (because it's part of all the default templates) is the appsettings.json file.
appsettings.json
appsettings.json, and the environment-specific appsettings.Development.json files are included in virtually every modern .NET app template, and provide an easy way to add values to the ASP.NET Core configuration system.
Hopefully it's no surprise that you can use these files to specify the URLs your app listens on using the same urls
, http_ports
and https_ports
keys you've seen already. The following adds the urls
key to the default template:
{
"urls": "http://*:5003", // 👈 Specify the URL
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
This version adds the http_ports
and https_ports
keys:
{
"http_ports": "5001;5002", // 👈 Expands to http://*:5001 and http://*:5002
"https_ports": "5003", // 👈 Expands to http2://*:5003
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
If you want to use different ports in development and production you can use the appsettings.json file for production, and the appsettings.Development.json for development. Or whatever pattern works for you.
Alternatively, you can rely on a different JSON file, launchSettings.json.
launchSettings.json
In addition to the appsettings.json file, most .NET project templates also include a launchSettings.json file in the Properties folder. This file doesn't add directly to your configuration; instead it contains various profiles for launching your ASP.NET Core application in development using dotnet run
.
A typical file contains one or more definitions for launching the profile directly from the command line and one definition for launching the profile using IIS Express. This file drives the Debug drop-down in Visual Studio:
launchSettings.json provides an easy way to set the application URLs via the applicationUrl
property - you can see one under the iisSettings
for IIS express, and one under each of the http
and https
profiles:
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:49412", // 👈 URL to use with IIS Express profile
"sslPort": 44381
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:5005", // 👈 HTTP-only profile
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7045;http://localhost:5005", // 👈 HTTP & HTTPS profile
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
You don't need to do anything special to use this file—dotnet run
will pick it up automatically.
launchSettings.json also provides an easy way to set additional environment variables using the
environmentVariables
, as you can see from the file above.
When you run your app from the command line with dotnet run
, your app will use the applicationUrl
properties in the "Project" command: http://localhost:5005
in the http
profile above. When you run the app using the "IISExpress" command, your app will use the applicationUrl
from the iisSettings.iisExpress
node: http://localhost:49412
.
This file is the easiest way to configure your environment when developing locally. In fact, you have to go out of your way to not use the launchSettings.json:
dotnet run --no-launch-profile
This will skip over the launchSettings.json file and fall back to configuration to determine the URLs instead.
All of the approaches shown so far set the URLs for Kestrel indirectly, but you can also set them directly.
KestrelServerOptions.Listen()
Kestrel is configured by default in most ASP.NET Core apps. If you wish, you can configure the endpoints for Kestrel manually, or by configuring KestrelServerOptions
using the IConfiguration
system, instead of using the higher-level configuration provided by ASPNETCORE_URLS
et al.
Kestrel is used by default when you self-host your application or when you run behind IIS out-of process. When you run in-process, an IIS HTTP Server is used. On Windows, you can also use the HTTP.sys kernel driver.
You're most likely to need to use the Kestrel Listen()
functions to configure HTTPS certificates, SSL/TLS protocols and cipher suites, and Server Name Indications (SNI) configurations. There's a lot of configuration options available, so for the most part I suggest referring to the documentation. As an example, you can use the Listen()
functions exposed by KestrelServerOptions
like this:
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(opts =>
{
opts.Listen(IPAddress.Loopback, port: 5002); // listen on http://localhost:5002
opts.ListenAnyIP(5003); // listen on http://*:5003
opts.ListenLocalhost(5004, listenOptions => listenOptions.UseHttps()); // listen on https://localhost:5004
opts.ListenAnyIP(5005, listenOptions => // listen on https://*:5005
{
listenOptions.UseHttps("testCert.pfx", "testPassword");
});
});
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
This configuration sets Kestrel listening on multiple addresses. When you set the URLs for kestrel in this way, it overrides the URLS
configuration value if you've set it through one of the other mechanisms, such as through environment variables. You'll see a warning in the logs if that happens:
warn: Microsoft.AspNetCore.Server.Kestrel[0]
Overriding address(es) 'http://localhost:5165'. Binding to endpoints defined via IConfiguration and/or UseKestrel() instead.
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://127.0.0.1:5002
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://[::]:5003
info: Microsoft.Hosting.Lifetime[14]
Now listening on: https://localhost:5004
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://[::]:5005
The Kestrel configuration was hard-coded in the example above, but it doesn't have to be—you can bind it using IConfiguration
instead, similar to how you can bind to urls
or http_ports
. For example, the above Kestrel configuration could be configured in appsettings.json instead:
{
"Kestrel": {
"Endpoints": {
"HttpLoopback": {
"Url": "http://localhost:5002"
},
"HttpAny": {
"Url": "http://*:5003"
},
"HttpsDefaultCert": {
"Url": "https://localhost:5004"
},
"HttpsInlineCertFile": {
"Url": "https://*:5005",
"Certificate": {
"Path": "testCert.pfx",
"Password": "testPassword"
}
}
}
}
}
This has the nice benefit of allowing complete configuration of your app's binding and HTTPS certificate configuration from the IConfiguration
. That means you can also take advantage of secure secret storage like Key Vault for storing your certificates and certificate passwords. If you use the appsettings.json configuration above, you don't need to call ConfigureKestrel()
at all.
Summary
In this post I showed eight different ways you can set the URLs that your application listens on. UseUrls()
and WebApplication.Urls
are the simplest, but generally aren't suitable for production workloads on their own. The --urls
, --http_ports
, and --https_ports
command line arguments and ASPNETCORE_
/DOTNET_
environment variables are most useful for setting the values in production. The launchSettings.json file is very useful for setting the URLs in a development environment. If you need fine-grained control over your configuration, you can use Kestrel's Listen*
options directly. These can also be loaded from configuration for easy use in both production and development.