Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

External Network Access to Kestrel and IIS Express in ASP.NET Core


:P
On this page:

External Access Stairs

Today I ported over my AlbumViewer sample Angular 2.0 application I worked on for a workshop for the last few weeks, to my ASP.NET Core project. I've been building various different kinds of backends for this Angular 2.0 front end app and it's been very easy to simply swap them out by changing the base URL to the API endpoints.

I've been working on the Angular 2.0 app on my Mac, but the server side ASP.NET I'm moving it to is running on Windows using ASP.NET Core and so I need to run my API application in a Windows VM on Parallels and access this API from the Mac side where the Angular application is running locally using the WebPack dev server.

In order to access the Windows API service from the Mac requires a bit of configuration as by default both Kestrel and IIS Express only serve requests on localhost. Both servers use default bindings that point explicitly at localhost which effectively disables remote access.

In order to make this work there are a couple of configuration steps required:

  • Change the default URL binding to a non-localhost address
  • Open the Firewall port
  • Map a host name to make it easier

Kestrel: What's the Problem? Url Bindings

The issue is that the default binding that Kestrel uses, binds explicitly to localhost. A localhost bound IP won't expose to the external network, so even though you might be able to access other ports on the VM over the network - like IIS running on port 80 - accessing of http://<WindowsVmIp>:5000/ is not supported without some configuration changes.

When Kestrel starts without any binding customization you get:

which clearly shows that it's using the localhost URL.

Override the default URLs

In order to expose Kestrel externally you either have to bind to a specific machine name, IP Address or 0.0.0.0 which stands for all IP Addresses (thanks to @DamianEdwards and @BradyMHolt for their help).

ASP.NET Core allows overriding the startup URLs as part of the startup process for the Web host and there are a number of ways that you can do this:

  • Set the –urls command line parameter
  • Use WebHost.UseUrls()
  • Set up hosting.json

You can override the start this via the launch command line:

dotnet run --urls http://0.0.0.0:5001

In order for this to work make sure that command line argument configuration is enabled as part of the startup procedure:

public static void Main(string[] args)
{
    // use this to allow command line parameters in the config
    var configuration = new ConfigurationBuilder()
        .AddCommandLine(args)
        .Build();
    ...
}

If you want more control you can also explicitly set the host Url or Urls. In code you can use the following - including reading a configuration setting from a custom command line parameter:

public static void Main(string[] args)
{
    // use this to allow command line parameters in the config
    var configuration = new ConfigurationBuilder()
        .AddCommandLine(args)
        .Build();


    var hostUrl = configuration["hosturl"];
    if (string.IsNullOrEmpty(hostUrl))
        hostUrl = "http://0.0.0.0:6000";


    var host = new WebHostBuilder()
        .UseKestrel()                
        .UseUrls(hostUrl)   // <!-- this 
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseIISIntegration()
        .UseStartup<Startup>()
        .UseConfiguration(configuration)
        .Build();

    host.Run();
}

With that in place you could run:

dotnet run                                 // default on port 6000
dotnet run --hosturl http://0.0.0.0:6001   // explicit

The key is the .UseUrls() call that applies the host url to the Webhost and as you can see you can apply logic and configuration to decide where the value comes from.

Note that you can specify multiple startup URLs separated by semicolons in the string passed.

Open your Firewall

Next you need to make sure that your Windows firewall allows access to the requested port.

netsh advfirewall firewall add rule name="Http Port 5000" dir=in action=allow protocol=TCP localport=5000

or you can use the interactive Windows Firewall application.

Assuming you used a non-localhost IP address or name, your application should now be externally accessible when you dotnet run.

On the Mac: Create a HOSTS entry

This step is optional, but when developing in a two OS environment I like to make it as easy as possible to identify machines, so I tend to create domain names for the server rather than using an IP address to access it by modifying the HOSTS file on the client machine. So I have dev.west-wind.com locally which maps to the IP address of my Windows virtual machine in Parallels.

Mac HOSTS file

With this in place I can now navigate to my app with:

http://dev.west-wind.com:5000/index.html

to get to my ASP.NET Core application on the Windows VM. Schweet!

Exposing IIS Express to the Network

If you want to use IIS Express and allow external access, you need to explicitly change the bindings in the solution applicationhost.config file which can be found in <solutionRoot>\.vs\config. Change the bindingInformation to:

<site name="AlbumViewerNetCore" id="2">
    <application path="/" applicationPool="Clr4IntegratedAppPool">
      <virtualDirectory path="/" physicalPath="C:\projects2010\AlbumViewerVNext\src\AlbumViewerNetCore" />
    </application>
    <bindings>          
      <binding protocol="http" bindingInformation="*:26448:*" />
      <binding protocol="https" bindingInformation="*:44319:*" />
    </bindings>
</site>

changing the *:26448:localhost to *:26448:* where 26448 is the IIS Express port.

Then run:

netsh http add urlacl url=http://*:26448/ user=Interactive listen=yes

to bind the http.sys entry.

Finally open the firewall to allow inbound connections to the specific IIS Express port:

netsh advfirewall firewall add rule name="Http Port 26448" dir=in action=allow protocol=TCP localport=26448

and you should be off to the races.

Acknowledgements

Many thanks to the following people who provided input to my earlier Twitter questions:

Image credit (stairs): © akiyoko / 123RF Stock Photo
this post created with Markdown Monster
Posted in ASP.NET  IIS  

The Voices of Reason


 

Andrew Lock
September 28, 2016

# re: External Network Access to Kestrel and IIS Express in ASP.NET Core

Nice comprehensive post, Rick, and thanks for the acknowledgement:)

I didn't know about the 'http://0.0.0.0' binding to bind to any host with a given port - you can also use 'http://*:5000' which seems to work fine, not sure if there are any subtle differences between them..

One other thing to mention when using parallels is you sometimes need to consider how the VM network is configured - as in whether you are using shared or bridged mode: http://kb.parallels.com/4948

Santosh Joshi
September 28, 2016

# re: External Network Access to Kestrel and IIS Express in ASP.NET Core

I was doing something similar recently. I had to allow a remote user to provide feedback on a project in my Windows machine. I ended up using ngrok, which avoided to provide my IP all the times. This setup seems to involving almost everything required for ngrok and will be enough for testing in a local network.

Andrei Rînea
September 29, 2016

# re: External Network Access to Kestrel and IIS Express in ASP.NET Core

Excellent coverage as usual!

Could you plan a future blog post on setting up nginx for production deployment of an ASP.NET Core site?

MedAnd
September 29, 2016

# re: External Network Access to Kestrel and IIS Express in ASP.NET Core

Not sure if this helps but using the following makes Kestrel listen to all IP addresses assigned to a machine:

.UseUrls("http://+:12345")


https://github.com/aspnet/KestrelHttpServer/issues/867

Jose Marcenaro
March 08, 2017

# re: External Network Access to Kestrel and IIS Express in ASP.NET Core

Thanks Rick for the comprehensive post.

As you may know, APIs hate blog posts, so they rapidly evolve to turn the posts obsolete 😉

In this case it is a tiny change: current versions of Kestrel do not use the "server.urls" parameter but "urls" instead.

So the updated line should say

"Set the --urls command line parameter".


Rick Strahl
March 08, 2017

# re: External Network Access to Kestrel and IIS Express in ASP.NET Core

@Jose - looks like both are working in Dotnet run tooling 1.0.


Jose
April 24, 2017

# re: External Network Access to Kestrel and IIS Express in ASP.NET Core

Excellent work AlbumViewer sample Angular 2.0 application

Its possible add infinite scroll?


Aroldo Peixe
April 27, 2017

# re: External Network Access to Kestrel and IIS Express in ASP.NET Core

Great article! How can I do on linux (Ubuntu 16.04)? Thank you.


Luke Puplett
September 21, 2017

# re: External Network Access to Kestrel and IIS Express in ASP.NET Core

I could not get this to work via command line arguments, it always showed in the console as listening on localhost.

I noticed while debugging what exactly ended up in my configuration bag when I discovered this

ASPNETCORE_URLS

I just set this environment variable in my project config Debug tab and it all works.


Leo
May 21, 2018

# re: External Network Access to Kestrel and IIS Express in ASP.NET Core

Hi Rick Very useful article, congratulations! In my case I was getting an "Invalid URI: The hostname could not be parsed" error when I changed the applicationhost.config file. So I solved it just removing the last asterisk:

Regards!


John
May 30, 2018

# re: External Network Access to Kestrel and IIS Express in ASP.NET Core

This didn't work for me for iisexpress

<binding protocol="http" bindingInformation="*:26448:*" />

Removing the asterisks did

<binding protocol="http" bindingInformation=":26448:" />

Mikey
August 16, 2018

# re: External Network Access to Kestrel and IIS Express in ASP.NET Core

is it possible to self host netcore without specifying the port (Just like IIS forwarding)


Rick Strahl
August 16, 2018

# re: External Network Access to Kestrel and IIS Express in ASP.NET Core

@mikey - you can specify port 80 as the self-host port which will then work without the port in the browser.

But you'll likely run into problems as something else (IIS? Skype?) will likely already be using port 80.


Adrian
October 30, 2018

# re: External Network Access to Kestrel and IIS Express in ASP.NET Core

As for binding to non-localhost URL in Kestrel, why not just modify the applicationUrl in launchSettings.json?


Jol Makahari
February 14, 2019

# re: External Network Access to Kestrel and IIS Express in ASP.NET Core

1.) Open port for incoming requests in FW 2.) setup Port forwarding in router 3.) Added: .UseUrls("http://0.0.0.0:15536") // added for external access .UseIISIntegration() // added for external access

4.) and executed: netsh http add urlacl url=http://*:15536/ user=Interactive listen=yes

It's working now, thanks!


Rick Strahl
February 14, 2019

# re: External Network Access to Kestrel and IIS Express in ASP.NET Core

@adrian - launchsettings just affects visual studio (and other tooling) launching, not runtime launching.


Lukas
March 21, 2020

# re: External Network Access to Kestrel and IIS Express in ASP.NET Core

Hi, Can i open the port programmatically in order to have a server that's accessable from outside out of the box?


Rick Strahl
March 23, 2020

# re: External Network Access to Kestrel and IIS Express in ASP.NET Core

@Lukas - you need to be an Administrator to do it so that's a problem if your app is not running as an Admin.


Lukas
March 23, 2020

# re: External Network Access to Kestrel and IIS Express in ASP.NET Core

Thank you for the quick reply! Sadly this seems like a deal-breaker for my project. Not to spam this thread an go too off-topic, but you seem to be quite the expert in this field so maybe you could direct me to a different approach: i wanted to have a simple pier to pier websocket application. (A client would start a edge kestrel and submit it's IP to a db where another client would then connect to the given IP) any ideas on how to achieve this in a .netcore environment?

Thanks


Gopinath Sundharam
April 16, 2020

# re: External Network Access to Kestrel and IIS Express in ASP.NET Core

Hi Rick, I should have found this article 2 days ago and saved a crap amount of time. You rock dude!


Swapniil
May 03, 2020

# re: External Network Access to Kestrel and IIS Express in ASP.NET Core

Hello Rick, Thanks a lot for all your posts. We are currently trying to deploy a .netcore api on server and facing a related issue.

  1. Local machine: IIS EXPRESS, IIS, dotnet cmd prompt - all works
  2. Server: dotnet cmd prompt works(port 5000,5001), IIS works when application pool is running with user credentials, but fails when we run it using applicationpoolidentity.

Any suggestions on what could be wrong.

Could this be related to corporate proxy, somehow applicationpoolidentity does not have access to proxy maybe ? If so, how could this be mitigated.

Thanks in advance.


Khang Azun
May 12, 2021

# re: External Network Access to Kestrel and IIS Express in ASP.NET Core

Dear Rich,

I was frustrated with exposing .NETCore webapp to external in MacOS for almost a day.

You saved my day. Thanks a lot. -Khang


West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024