ASP.NET Core and IIS

By Dawid on (tags: ASP.NET, core, iis, categories: code)

What does it means to host you application in IIS? To help with that there is new module called ASP.NET Core Module.


It’s native IIS module that forwards request to your application. It plugs into IIS, receive requests and then it’s forwarding this into process which we want to be started to handle that request. Module does all bunch of things like:

  • Supports forwarding client certificates
  • Windows authentication
  • IIS sub applications
  • App offline
  • Logging


To be able to use it we have to configure it in the wbe.config file. That that’s really the only reason why there is still a web.config file! It’s used to set up this module.

Next thing we have to point IIS where our content root is. Static files will only be server from the web root.

And the last thing is to setup IIS integration for the ASP.NET Core host. It’s required to receive requests from IIS. That is pretty simple:

   1: webHostBuilder.UserIISIntegration()

That extension method is available in nuget package.


What actually the ASP.NET Core Module (ACM) does:

  1. IIS receives a request
  2. ACM starts the app in a separate process (if it’s not already running) and pass there all arguments – this is the place where application is no longer running as w3wp.exe but as dotnet.exe (if we are developing windows application that it can be mySuperApplication.exe process
  3. ACM forwards the request to the application – at that point Kestrel is receiving the request
  4. The app process the request and sends the response back to ACM
  5. IIS forwards the response to the client

Here is how the configuration looks like in the web.config file:

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <configuration>
   3:   <system.webServer>
   4:     <handlers>
   5:       <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/>
   6:     </handlers>
   7:     <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/>
   8:   </system.webServer>
   9: </configuration>

Environment variables set by ACM for the host process

There are 3 environment variables which says Kestrel where and what she be listen for:

  • ASPNETCORE_PORT – the port to forward the request
  • ASPNETCORE_APPL_PATH – application path to forward the request
  • ASPNETCORE_TOKEN - pairing token to endure only local requests from ISS get received. That token will be stored in request header.

We can spy those variables by writing simple application:

   1: public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
   2: {
   3:     loggerFactory.AddConsole();
   5:     if (env.IsDevelopment())
   6:     {
   7:         app.UseDeveloperExceptionPage();
   8:     }
  10:     app.Run(async (context) =>
  11:     {
  12:         var configuration = new ConfigurationBuilder().AddEnvironmentVariables().Build();                
  13:         var addresses = app.ServerFeatures.Get<IServerAddressesFeature>().Addresses;
  15:         context.Response.ContentType = "text/plain";
  17:         foreach (string address  in addresses)
  18:         {
  19:             await context.Response.WriteAsync(address + Environment.NewLine);
  20:         }
  22:         await context.Response.WriteAsync(configuration["ASPNETCORE_PORT"] + Environment.NewLine);
  23:         await context.Response.WriteAsync(configuration["ASPNETCORE_APPL_PATH"] + Environment.NewLine);
  24:         await context.Response.WriteAsync(configuration["ASPNETCORE_TOKEN "] + Environment.NewLine);
  25:     });
  26: }

Wen we will run this application from Visual Studio we will see something like that:


In the browser URL you will see http://localhost:26775/ that is address on which IISExpress is running on. On port 26775 IISExpress is listening for the requests. When the request comes, the ASP.NET Core Module will saw it and pass it to the Kestrel which is listening on 31240. Bellow the address you will find 3 environment variables which was set up by ACM.

We can also observe that hand shake token in the request headers by setting up breakpoint in the Startup.cs class:




When you publish an application in .NET Core world command dotnet publish is called. That command is for example collecting all the nugget packages putting them in one folder so that they will be available where ever you will take your application.

Of course publish can be done from Visual Studio:


Now, when publication process will be finished you can go the the publication directory and take a look at the web.config file. You will see that it’s looks a little bit different:

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <configuration>
   3:   <system.webServer>
   4:     <handlers>
   5:       <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
   6:     </handlers>
   7:     <aspNetCore processPath="dotnet" arguments=".\MyApplication.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" />
   8:   </system.webServer>
   9: </configuration>
The strange placeholders - %LAUNCHER_PATH% and %LAUNCHER_ARGS% - has been replaced. processPath now tell which process have to be run when request comes. The arguments tells which dll have to be run by dotnet process. 

What actually had replaced those placeholders? When you will open the project.json file you will see tools section:

   1: "tools": {
   2:   "Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0"
   3: }

and at the end:

   1: "scripts": {
   2:   "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
   3: }

there is post publish step. When the publish will be done that script will be run and above tool will be run (dotnet publish-iis) which will fill web.config file.

Configuring IIS

Now let’s run IIS and let’s add new application. Pont you application directory and remember to configure application pool to use “No managed code” mode as all required stuff are in the application directory.

And that’s it!