HTTP.sys web server implementation in ASP.NET Core
By Tom Dykstra and Chris Ross
HTTP.sys is a web server for ASP.NET Core that only runs on Windows. HTTP.sys is an alternative to Kestrel server and offers some features that Kestrel doesn't provide.
Important
HTTP.sys isn't compatible with the ASP.NET Core Module and can't be used with IIS or IIS Express.
HTTP.sys supports the following features:
- Windows Authentication
- Port sharing
- HTTPS with SNI
- HTTP/2 over TLS (Windows 10 or later)
- Direct file transmission
- Response caching
- WebSockets (Windows 8 or later)
Supported Windows versions:
- Windows 7 or later
- Windows Server 2008 R2 or later
View or download sample code (how to download)
When to use HTTP.sys
HTTP.sys is useful for deployments where:
There's a need to expose the server directly to the Internet without using IIS.
An internal deployment requires a feature not available in Kestrel. For more information, see Kestrel vs. HTTP.sys
HTTP.sys is mature technology that protects against many types of attacks and provides the robustness, security, and scalability of a full-featured web server. IIS itself runs as an HTTP listener on top of HTTP.sys.
HTTP/2 support
HTTP/2 is enabled for ASP.NET Core apps when the following base requirements are met:
- Windows Server 2016/Windows 10 or later
- Application-Layer Protocol Negotiation (ALPN) connection
- TLS 1.2 or later connection
If an HTTP/2 connection is established, HttpRequest.Protocol reports HTTP/2
.
HTTP/2 is enabled by default. If an HTTP/2 connection isn't established, the connection falls back to HTTP/1.1. In a future release of Windows, HTTP/2 configuration flags will be available, including the ability to disable HTTP/2 with HTTP.sys.
HTTP/3 support
HTTP/3 is enabled for ASP.NET Core apps when the following base requirements are met:
- Windows Server 2022/Windows 11 or later
- An
https
url binding is used. - The EnableHttp3 registry key is set.
The preceding Windows 11 Build versions may require the use of a Windows Insider build.
HTTP/3 is discovered as an upgrade from HTTP/1.1 or HTTP/2 via the alt-svc
header. That means the first request will normally use HTTP/1.1 or HTTP/2 before switching to HTTP/3. Http.Sys doesn't automatically add the alt-svc
header, it must be added by the application. The following code is a middleware example that adds the alt-svc
response header.
app.Use((context, next) =>
{
context.Response.Headers.AltSvc = "h3=\":443\"";
return next(context);
});
Place the preceding code early in the request pipeline.
Http.Sys also supports sending an AltSvc HTTP/2 protocol message rather than a response header to notify the client that HTTP/3 is available. See the EnableAltSvc registry key. This requires netsh sslcert bindings that use host names rather than IP addresses.
Kernel mode authentication with Kerberos
HTTP.sys delegates to kernel mode authentication with the Kerberos authentication protocol. User mode authentication isn't supported with Kerberos and HTTP.sys. The machine account must be used to decrypt the Kerberos token/ticket that's obtained from Active Directory and forwarded by the client to the server to authenticate the user. Register the Service Principal Name (SPN) for the host, not the user of the app.
Support for kernel-mode response buffering
In some scenarios, high volumes of small writes with high latency can cause significant performance impact to HTTP.sys
. This impact is due to the lack of a Pipe buffer in the HTTP.sys
implementation. To improve performance in these scenarios, support for response buffering is included in HTTP.sys
. Enable buffering by setting HttpSysOptions.EnableKernelResponseBuffering to true
.
Response buffering should be enabled by an app that does synchronous I/O, or asynchronous I/O with no more than one outstanding write at a time. In these scenarios, response buffering can significantly improve throughput over high-latency connections.
Apps that use asynchronous I/O and that may have more than one write outstanding at a time should not use this flag. Enabling this flag can result in higher CPU and memory usage by HTTP.Sys.
How to use HTTP.sys
Configure the ASP.NET Core app to use HTTP.sys
Call the UseHttpSys extension method when building the host, specifying any required HttpSysOptions. The following example sets options to their default values:
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.HttpSys;
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.UseHttpSys(options =>
{
options.AllowSynchronousIO = false;
options.Authentication.Schemes = AuthenticationSchemes.None;
options.Authentication.AllowAnonymous = true;
options.MaxConnections = null;
options.MaxRequestBodySize = 30_000_000;
options.UrlPrefixes.Add("http://localhost:5005");
});
builder.Services.AddRazorPages();
var app = builder.Build();
Additional HTTP.sys configuration is handled through registry settings.
For more information about HTTP.sys options, see HttpSysOptions.
MaxRequestBodySize
The maximum allowed size of any request body in bytes. When set to null
, the maximum request body size is unlimited. This limit has no effect on upgraded connections, which are always unlimited.
The recommended method to override the limit in an ASP.NET Core MVC app for a single IActionResult
is to use the RequestSizeLimitAttribute attribute on an action method:
[RequestSizeLimit(100000000)]
public IActionResult MyActionMethod()
An exception is thrown if the app attempts to configure the limit on a request after the app has started reading the request. An IsReadOnly
property can be used to indicate if the MaxRequestBodySize
property is in a read-only state, meaning it's too late to configure the limit.
If the app should override MaxRequestBodySize per-request, use the IHttpMaxRequestBodySizeFeature:
app.Use((context, next) =>
{
context.Features.GetRequiredFeature<IHttpMaxRequestBodySizeFeature>()
.MaxRequestBodySize = 10 * 1024;
var server = context.RequestServices
.GetRequiredService<IServer>();
var serverAddressesFeature = server.Features
.GetRequiredFeature<IServerAddressesFeature>();
var addresses = string.Join(", ", serverAddressesFeature.Addresses);
var loggerFactory = context.RequestServices
.GetRequiredService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger("Sample");
logger.LogInformation("Addresses: {addresses}", addresses);
return next(context);
});
If using Visual Studio, make sure the app isn't configured to run IIS or IIS Express.
In Visual Studio, the default launch profile is for IIS Express. To run the project as a console app, manually change the selected profile, as shown in the following screenshot:
Configure Windows Server
Determine the ports to open for the app and use Windows Firewall or the New-NetFirewallRule PowerShell cmdlet to open firewall ports to allow traffic to reach HTTP.sys. In the following commands and app configuration, port 443 is used.
When deploying to an Azure VM, open the ports in the Network Security Group. In the following commands and app configuration, port 443 is used.
Obtain and install X.509 certificates, if required.
On Windows, create self-signed certificates using the New-SelfSignedCertificate PowerShell cmdlet. For an unsupported example, see UpdateIISExpressSSLForChrome.ps1.
Install either self-signed or CA-signed certificates in the server's Local Machine > Personal store.
If the app is a framework-dependent deployment, install .NET Core, .NET Framework, or both (if the app is a .NET Core app targeting the .NET Framework).
- .NET Core: If the app requires .NET Core, obtain and run the .NET Core Runtime installer from .NET Core Downloads. Don't install the full SDK on the server.
- .NET Framework: If the app requires .NET Framework, see the .NET Framework installation guide. Install the required .NET Framework. The installer for the latest .NET Framework is available from the .NET Core Downloads page.
If the app is a self-contained deployment, the app includes the runtime in its deployment. No framework installation is required on the server.
Configure URLs and ports in the app.
By default, ASP.NET Core binds to
http://localhost:5000
. To configure URL prefixes and ports, options include:- UseUrls
urls
command-line argumentASPNETCORE_URLS
environment variable- UrlPrefixes
The following code example shows how to use UrlPrefixes with the server's local IP address
10.0.0.4
on port 443:var builder = WebApplication.CreateBuilder(args); builder.WebHost.UseHttpSys(options => { options.UrlPrefixes.Add("https://10.0.0.4:443"); }); builder.Services.AddRazorPages(); var app = builder.Build();
An advantage of
UrlPrefixes
is that an error message is generated immediately for improperly formatted prefixes.The settings in
UrlPrefixes
overrideUseUrls
/urls
/ASPNETCORE_URLS
settings. Therefore, an advantage ofUseUrls
,urls
, and theASPNETCORE_URLS
environment variable is that it's easier to switch between Kestrel and HTTP.sys.HTTP.sys recognizes two types of wild cards in URL prefixes:
*
is a weak binding, also known as a fallback binding. If the URL prefix ishttp://*:5000
, and something else is bound to port 5000, this binding won't be used.+
is a strong binding. If the URL prefix ishttp://+:5000
, this binding will be used before other port 5000 bindings.
For more information, see UrlPrefix Strings.
Warning
Top-level wildcard bindings (
http://*:80/
andhttp://+:80
) should not be used. Top-level wildcard bindings create app security vulnerabilities. This applies to both strong and weak wildcards. Use explicit host names or IP addresses rather than wildcards. Subdomain wildcard binding (for example,*.mysub.com
) isn't a security risk if you control the entire parent domain (as opposed to*.com
, which is vulnerable). For more information, see RFC 9110: Section 7.2: Host and :authority.Apps and containers are often given only a port to listen on, like port 80, without additional constraints like host or path. HTTP_PORTS and HTTPS_PORTS are config keys that specify the listening ports for the Kestrel and HTTP.sys servers. These keys may be specified as environment variables defined with the
DOTNET_
orASPNETCORE_
prefixes, or specified directly through any other config input, such asappsettings.json
. Each is a semicolon-delimited list of port values, as shown in the following example:ASPNETCORE_HTTP_PORTS=80;8080 ASPNETCORE_HTTPS_PORTS=443;8081
The preceding example is shorthand for the following configuration, which specifies the scheme (HTTP or HTTPS) and any host or IP.
ASPNETCORE_URLS=http://*:80/;http://*:8080/;https://*:443/;https://*:8081/
The HTTP_PORTS and HTTPS_PORTS configuration keys are lower priority and are overridden by URLS or values provided directly in code. Certificates still need to be configured separately via server-specific mechanics for HTTPS.
These configuration keys are equivalent to top-level wildcard bindings. They're convenient for development and container scenarios, but avoid wildcards when running on a machine that may also host other services.
Preregister URL prefixes on the server.
The built-in tool for configuring HTTP.sys is netsh.exe. netsh.exe is used to reserve URL prefixes and assign X.509 certificates. The tool requires administrator privileges.
Use the netsh.exe tool to register URLs for the app:
netsh http add urlacl url=<URL> user=<USER>
<URL>
: The fully qualified Uniform Resource Locator (URL). Don't use a wildcard binding. Use a valid hostname or local IP address. The URL must include a trailing slash.<USER>
: Specifies the user or user-group name.
In the following example, the local IP address of the server is
10.0.0.4
:netsh http add urlacl url=https://10.0.0.4:443/ user=Users
When a URL is registered, the tool responds with
URL reservation successfully added
.To delete a registered URL, use the
delete urlacl
command:netsh http delete urlacl url=<URL>
Register X.509 certificates on the server.
Use the netsh.exe tool to register certificates for the app:
netsh http add sslcert ipport=<IP>:<PORT> certhash=<THUMBPRINT> appid="{<GUID>}"
<IP>
: Specifies the local IP address for the binding. Don't use a wildcard binding. Use a valid IP address.<PORT>
: Specifies the port for the binding.<THUMBPRINT>
: The X.509 certificate thumbprint.<GUID>
: A developer-generated GUID to represent the app for informational purposes.
For reference purposes, store the GUID in the app as a package tag:
- In Visual Studio:
- Open the app's project properties by right-clicking on the app in Solution Explorer and selecting Properties.
- Select the Package tab.
- Enter the GUID that you created in the Tags field.
- When not using Visual Studio:
Open the app's project file.
Add a
<PackageTags>
property to a new or existing<PropertyGroup>
with the GUID that you created:<PropertyGroup> <PackageTags>9412ee86-c21b-4eb8-bd89-f650fbf44931</PackageTags> </PropertyGroup>
In the following example:
- The local IP address of the server is
10.0.0.4
. - An online random GUID generator provides the
appid
value.
netsh http add sslcert ipport=10.0.0.4:443 certhash=b66ee04419d4ee37464ab8785ff02449980eae10 appid="{9412ee86-c21b-4eb8-bd89-f650fbf44931}"
When a certificate is registered, the tool responds with
SSL Certificate successfully added
.To delete a certificate registration, use the
delete sslcert
command:netsh http delete sslcert ipport=<IP>:<PORT>
Reference documentation for netsh.exe:
Run the app.
Administrator privileges aren't required to run the app when binding to localhost using HTTP (not HTTPS) with a port number greater than 1024. For other configurations (for example, using a local IP address or binding to port 443), run the app with administrator privileges.
The app responds at the server's public IP address. In this example, the server is reached from the Internet at its public IP address of
104.214.79.47
.A development certificate is used in this example. The page loads securely after bypassing the browser's untrusted certificate warning.
Proxy server and load balancer scenarios
For apps hosted by HTTP.sys that interact with requests from the Internet or a corporate network, additional configuration might be required when hosting behind proxy servers and load balancers. For more information, see Configure ASP.NET Core to work with proxy servers and load balancers.
Get detailed timing information with IHttpSysRequestTimingFeature
IHttpSysRequestTimingFeature provides detailed timing information for requests:
- Timestamps are obtained using QueryPerformanceCounter.
- The timestamp frequency can be obtained via QueryPerformanceFrequency.
- The index of the timing can be cast to HttpSysRequestTimingType to know what the timing represents.
- The value may be 0 if the timing isn't available for the current request.
- Requires Windows 10 version 2004, Windows Server 2022, or later.
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.HttpSys;
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.UseHttpSys();
var app = builder.Build();
app.Use((context, next) =>
{
var feature = context.Features.GetRequiredFeature<IHttpSysRequestTimingFeature>();
var loggerFactory = context.RequestServices.GetRequiredService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger("Sample");
var timestamps = feature.Timestamps;
for (var i = 0; i < timestamps.Length; i++)
{
var timestamp = timestamps[i];
var timingType = (HttpSysRequestTimingType)i;
logger.LogInformation("Timestamp {timingType}: {timestamp}",
timingType, timestamp);
}
return next(context);
});
app.MapGet("/", () => Results.Ok());
app.Run();
IHttpSysRequestTimingFeature.TryGetTimestamp retrieves the timestamp for the provided timing type:
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.HttpSys;
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.UseHttpSys();
var app = builder.Build();
app.Use((context, next) =>
{
var feature = context.Features.GetRequiredFeature<IHttpSysRequestTimingFeature>();
var loggerFactory = context.RequestServices.GetRequiredService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger("Sample");
var timingType = HttpSysRequestTimingType.RequestRoutingEnd;
if (feature.TryGetTimestamp(timingType, out var timestamp))
{
logger.LogInformation("Timestamp {timingType}: {timestamp}",
timingType, timestamp);
}
else
{
logger.LogInformation("Timestamp {timingType}: not available for the "
+ "current request", timingType);
}
return next(context);
});
app.MapGet("/", () => Results.Ok());
app.Run();
IHttpSysRequestTimingFeature.TryGetElapsedTime gives the elapsed time between two specified timings:
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.HttpSys;
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.UseHttpSys();
var app = builder.Build();
app.Use((context, next) =>
{
var feature = context.Features.GetRequiredFeature<IHttpSysRequestTimingFeature>();
var loggerFactory = context.RequestServices.GetRequiredService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger("Sample");
var startingTimingType = HttpSysRequestTimingType.RequestRoutingStart;
var endingTimingType = HttpSysRequestTimingType.RequestRoutingEnd;
if (feature.TryGetElapsedTime(startingTimingType, endingTimingType, out var elapsed))
{
logger.LogInformation(
"Elapsed time {startingTimingType} to {endingTimingType}: {elapsed}",
startingTimingType,
endingTimingType,
elapsed);
}
else
{
logger.LogInformation(
"Elapsed time {startingTimingType} to {endingTimingType}:"
+ " not available for the current request.",
startingTimingType,
endingTimingType);
}
return next(context);
});
app.MapGet("/", () => Results.Ok());
app.Run();
Advanced HTTP/2 features to support gRPC
Additional HTTP/2 features in HTTP.sys support gRPC, including support for response trailers and sending reset frames.
Requirements to run gRPC with HTTP.sys:
- Windows 11 Build 22000 or later, Windows Server 2022 Build 20348 or later.
- TLS 1.2 or later connection.
Trailers
HTTP Trailers are similar to HTTP Headers, except they are sent after the response body is sent. For IIS and HTTP.sys, only HTTP/2 response trailers are supported.
if (httpContext.Response.SupportsTrailers())
{
httpContext.Response.DeclareTrailer("trailername");
// Write body
httpContext.Response.WriteAsync("Hello world");
httpContext.Response.AppendTrailer("trailername", "TrailerValue");
}
In the preceding example code:
SupportsTrailers
ensures that trailers are supported for the response.DeclareTrailer
adds the given trailer name to theTrailer
response header. Declaring a response's trailers is optional, but recommended. IfDeclareTrailer
is called, it must be before the response headers are sent.AppendTrailer
appends the trailer.
Reset
Reset allows for the server to reset a HTTP/2 request with a specified error code. A reset request is considered aborted.
var resetFeature = httpContext.Features.Get<IHttpResetFeature>();
resetFeature.Reset(errorCode: 2);
Reset
in the preceding code example specifies the INTERNAL_ERROR
error code. For more information about HTTP/2 error codes, visit the HTTP/2 specification error code section.
Tracing
For information about how to get traces from HTTP.sys, see HTTP.sys Manageability Scenarios.
Additional resources
HTTP.sys is a web server for ASP.NET Core that only runs on Windows. HTTP.sys is an alternative to Kestrel server and offers some features that Kestrel doesn't provide.
Important
HTTP.sys isn't compatible with the ASP.NET Core Module and can't be used with IIS or IIS Express.
HTTP.sys supports the following features:
- Windows Authentication
- Port sharing
- HTTPS with SNI
- HTTP/2 over TLS (Windows 10 or later)
- Direct file transmission
- Response caching
- WebSockets (Windows 8 or later)
Supported Windows versions:
- Windows 7 or later
- Windows Server 2008 R2 or later
View or download sample code (how to download)
When to use HTTP.sys
HTTP.sys is useful for deployments where:
There's a need to expose the server directly to the Internet without using IIS.
An internal deployment requires a feature not available in Kestrel. For more information, see Kestrel vs. HTTP.sys
HTTP.sys is mature technology that protects against many types of attacks and provides the robustness, security, and scalability of a full-featured web server. IIS itself runs as an HTTP listener on top of HTTP.sys.
HTTP/2 support
HTTP/2 is enabled for ASP.NET Core apps when the following base requirements are met:
- Windows Server 2016/Windows 10 or later
- Application-Layer Protocol Negotiation (ALPN) connection
- TLS 1.2 or later connection
If an HTTP/2 connection is established, HttpRequest.Protocol reports HTTP/2
.
HTTP/2 is enabled by default. If an HTTP/2 connection isn't established, the connection falls back to HTTP/1.1. In a future release of Windows, HTTP/2 configuration flags will be available, including the ability to disable HTTP/2 with HTTP.sys.
HTTP/3 support
HTTP/3 is enabled for ASP.NET Core apps when the following base requirements are met:
- Windows Server 2022/Windows 11 or later
- An
https
url binding is used. - The EnableHttp3 registry key is set.
The preceding Windows 11 Build versions may require the use of a Windows Insider build.
HTTP/3 is discovered as an upgrade from HTTP/1.1 or HTTP/2 via the alt-svc
header. That means the first request will normally use HTTP/1.1 or HTTP/2 before switching to HTTP/3. Http.Sys doesn't automatically add the alt-svc
header, it must be added by the application. The following code is a middleware example that adds the alt-svc
response header.
app.Use((context, next) =>
{
context.Response.Headers.AltSvc = "h3=\":443\"";
return next(context);
});
Place the preceding code early in the request pipeline.
Http.Sys also supports sending an AltSvc HTTP/2 protocol message rather than a response header to notify the client that HTTP/3 is available. See the EnableAltSvc registry key. This requires netsh sslcert bindings that use host names rather than IP addresses.
Kernel mode authentication with Kerberos
HTTP.sys delegates to kernel mode authentication with the Kerberos authentication protocol. User mode authentication isn't supported with Kerberos and HTTP.sys. The machine account must be used to decrypt the Kerberos token/ticket that's obtained from Active Directory and forwarded by the client to the server to authenticate the user. Register the Service Principal Name (SPN) for the host, not the user of the app.
How to use HTTP.sys
Configure the ASP.NET Core app to use HTTP.sys
Call the UseHttpSys extension method when building the host, specifying any required HttpSysOptions. The following example sets options to their default values:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseHttpSys(options =>
{
options.AllowSynchronousIO = false;
options.Authentication.Schemes = AuthenticationSchemes.None;
options.Authentication.AllowAnonymous = true;
options.MaxConnections = null;
options.MaxRequestBodySize = 30000000;
options.UrlPrefixes.Add("http://localhost:5005");
});
webBuilder.UseStartup<Startup>();
});
Additional HTTP.sys configuration is handled through registry settings.
For more information about HTTP.sys options, see HttpSysOptions.
MaxRequestBodySize
The maximum allowed size of any request body in bytes. When set to null
, the maximum request body size is unlimited. This limit has no effect on upgraded connections, which are always unlimited.
The recommended method to override the limit in an ASP.NET Core MVC app for a single IActionResult
is to use the RequestSizeLimitAttribute attribute on an action method:
[RequestSizeLimit(100000000)]
public IActionResult MyActionMethod()
An exception is thrown if the app attempts to configure the limit on a request after the app has started reading the request. An IsReadOnly
property can be used to indicate if the MaxRequestBodySize
property is in a read-only state, meaning it's too late to configure the limit.
If the app should override MaxRequestBodySize per-request, use the IHttpMaxRequestBodySizeFeature:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env,
ILogger<Startup> logger, IServer server)
{
app.Use(async (context, next) =>
{
context.Features.Get<IHttpMaxRequestBodySizeFeature>()
.MaxRequestBodySize = 10 * 1024;
var serverAddressesFeature =
app.ServerFeatures.Get<IServerAddressesFeature>();
var addresses = string.Join(", ", serverAddressesFeature?.Addresses);
logger.LogInformation("Addresses: {Addresses}", addresses);
await next.Invoke();
});
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
If using Visual Studio, make sure the app isn't configured to run IIS or IIS Express.
In Visual Studio, the default launch profile is for IIS Express. To run the project as a console app, manually change the selected profile, as shown in the following screenshot:
Configure Windows Server
Determine the ports to open for the app and use Windows Firewall or the New-NetFirewallRule PowerShell cmdlet to open firewall ports to allow traffic to reach HTTP.sys. In the following commands and app configuration, port 443 is used.
When deploying to an Azure VM, open the ports in the Network Security Group. In the following commands and app configuration, port 443 is used.
Obtain and install X.509 certificates, if required.
On Windows, create self-signed certificates using the New-SelfSignedCertificate PowerShell cmdlet. For an unsupported example, see UpdateIISExpressSSLForChrome.ps1.
Install either self-signed or CA-signed certificates in the server's Local Machine > Personal store.
If the app is a framework-dependent deployment, install .NET Core, .NET Framework, or both (if the app is a .NET Core app targeting the .NET Framework).
- .NET Core: If the app requires .NET Core, obtain and run the .NET Core Runtime installer from .NET Core Downloads. Don't install the full SDK on the server.
- .NET Framework: If the app requires .NET Framework, see the .NET Framework installation guide. Install the required .NET Framework. The installer for the latest .NET Framework is available from the .NET Core Downloads page.
If the app is a self-contained deployment, the app includes the runtime in its deployment. No framework installation is required on the server.
Configure URLs and ports in the app.
By default, ASP.NET Core binds to
http://localhost:5000
. To configure URL prefixes and ports, options include:- UseUrls
urls
command-line argumentASPNETCORE_URLS
environment variable- UrlPrefixes
The following code example shows how to use UrlPrefixes with the server's local IP address
10.0.0.4
on port 443:public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseHttpSys(options => { options.UrlPrefixes.Add("https://10.0.0.4:443"); }); webBuilder.UseStartup<Startup>(); });
An advantage of
UrlPrefixes
is that an error message is generated immediately for improperly formatted prefixes.The settings in
UrlPrefixes
overrideUseUrls
/urls
/ASPNETCORE_URLS
settings. Therefore, an advantage ofUseUrls
,urls
, and theASPNETCORE_URLS
environment variable is that it's easier to switch between Kestrel and HTTP.sys.HTTP.sys uses the HTTP Server API UrlPrefix string formats.
Warning
Top-level wildcard bindings (
http://*:80/
andhttp://+:80
) should not be used. Top-level wildcard bindings create app security vulnerabilities. This applies to both strong and weak wildcards. Use explicit host names or IP addresses rather than wildcards. Subdomain wildcard binding (for example,*.mysub.com
) isn't a security risk if you control the entire parent domain (as opposed to*.com
, which is vulnerable). For more information, see RFC 9110: Section 7.2: Host and :authority.Preregister URL prefixes on the server.
The built-in tool for configuring HTTP.sys is netsh.exe. netsh.exe is used to reserve URL prefixes and assign X.509 certificates. The tool requires administrator privileges.
Use the netsh.exe tool to register URLs for the app:
netsh http add urlacl url=<URL> user=<USER>
<URL>
: The fully qualified Uniform Resource Locator (URL). Don't use a wildcard binding. Use a valid hostname or local IP address. The URL must include a trailing slash.<USER>
: Specifies the user or user-group name.
In the following example, the local IP address of the server is
10.0.0.4
:netsh http add urlacl url=https://10.0.0.4:443/ user=Users
When a URL is registered, the tool responds with
URL reservation successfully added
.To delete a registered URL, use the
delete urlacl
command:netsh http delete urlacl url=<URL>
Register X.509 certificates on the server.
Use the netsh.exe tool to register certificates for the app:
netsh http add sslcert ipport=<IP>:<PORT> certhash=<THUMBPRINT> appid="{<GUID>}"
<IP>
: Specifies the local IP address for the binding. Don't use a wildcard binding. Use a valid IP address.<PORT>
: Specifies the port for the binding.<THUMBPRINT>
: The X.509 certificate thumbprint.<GUID>
: A developer-generated GUID to represent the app for informational purposes.
For reference purposes, store the GUID in the app as a package tag:
- In Visual Studio:
- Open the app's project properties by right-clicking on the app in Solution Explorer and selecting Properties.
- Select the Package tab.
- Enter the GUID that you created in the Tags field.
- When not using Visual Studio:
Open the app's project file.
Add a
<PackageTags>
property to a new or existing<PropertyGroup>
with the GUID that you created:<PropertyGroup> <PackageTags>9412ee86-c21b-4eb8-bd89-f650fbf44931</PackageTags> </PropertyGroup>
In the following example:
- The local IP address of the server is
10.0.0.4
. - An online random GUID generator provides the
appid
value.
netsh http add sslcert ipport=10.0.0.4:443 certhash=b66ee04419d4ee37464ab8785ff02449980eae10 appid="{9412ee86-c21b-4eb8-bd89-f650fbf44931}"
When a certificate is registered, the tool responds with
SSL Certificate successfully added
.To delete a certificate registration, use the
delete sslcert
command:netsh http delete sslcert ipport=<IP>:<PORT>
Reference documentation for netsh.exe:
Run the app.
Administrator privileges aren't required to run the app when binding to localhost using HTTP (not HTTPS) with a port number greater than 1024. For other configurations (for example, using a local IP address or binding to port 443), run the app with administrator privileges.
The app responds at the server's public IP address. In this example, the server is reached from the Internet at its public IP address of
104.214.79.47
.A development certificate is used in this example. The page loads securely after bypassing the browser's untrusted certificate warning.
Proxy server and load balancer scenarios
For apps hosted by HTTP.sys that interact with requests from the Internet or a corporate network, additional configuration might be required when hosting behind proxy servers and load balancers. For more information, see Configure ASP.NET Core to work with proxy servers and load balancers.
Advanced HTTP/2 features to support gRPC
Additional HTTP/2 features in HTTP.sys support gRPC, including support for response trailers and sending reset frames.
Requirements to run gRPC with HTTP.sys:
- Windows 11 Build 22000 or later, Windows Server 2022 Build 20348 or later.
- TLS 1.2 or later connection.
Trailers
HTTP Trailers are similar to HTTP Headers, except they are sent after the response body is sent. For IIS and HTTP.sys, only HTTP/2 response trailers are supported.
if (httpContext.Response.SupportsTrailers())
{
httpContext.Response.DeclareTrailer("trailername");
// Write body
httpContext.Response.WriteAsync("Hello world");
httpContext.Response.AppendTrailer("trailername", "TrailerValue");
}
In the preceding example code:
SupportsTrailers
ensures that trailers are supported for the response.DeclareTrailer
adds the given trailer name to theTrailer
response header. Declaring a response's trailers is optional, but recommended. IfDeclareTrailer
is called, it must be before the response headers are sent.AppendTrailer
appends the trailer.
Reset
Reset allows for the server to reset a HTTP/2 request with a specified error code. A reset request is considered aborted.
var resetFeature = httpContext.Features.Get<IHttpResetFeature>();
resetFeature.Reset(errorCode: 2);
Reset
in the preceding code example specifies the INTERNAL_ERROR
error code. For more information about HTTP/2 error codes, visit the HTTP/2 specification error code section.
Additional resources
HTTP.sys is a web server for ASP.NET Core that only runs on Windows. HTTP.sys is an alternative to Kestrel server and offers some features that Kestrel doesn't provide.
Important
HTTP.sys isn't compatible with the ASP.NET Core Module and can't be used with IIS or IIS Express.
HTTP.sys supports the following features:
- Windows Authentication
- Port sharing
- HTTPS with SNI
- HTTP/2 over TLS (Windows 10 or later)
- Direct file transmission
- Response caching
- WebSockets (Windows 8 or later)
Supported Windows versions:
- Windows 7 or later
- Windows Server 2008 R2 or later
View or download sample code (how to download)
When to use HTTP.sys
HTTP.sys is useful for deployments where:
There's a need to expose the server directly to the Internet without using IIS.
An internal deployment requires a feature not available in Kestrel. For more information, see Kestrel vs. HTTP.sys
HTTP.sys is mature technology that protects against many types of attacks and provides the robustness, security, and scalability of a full-featured web server. IIS itself runs as an HTTP listener on top of HTTP.sys.
HTTP/2 support
HTTP/2 is enabled for ASP.NET Core apps if the following base requirements are met:
- Windows Server 2016/Windows 10 or later
- Application-Layer Protocol Negotiation (ALPN) connection
- TLS 1.2 or later connection
If an HTTP/2 connection is established, HttpRequest.Protocol reports HTTP/2
.
HTTP/2 is enabled by default. If an HTTP/2 connection isn't established, the connection falls back to HTTP/1.1. In a future release of Windows, HTTP/2 configuration flags will be available, including the ability to disable HTTP/2 with HTTP.sys.
Kernel mode authentication with Kerberos
HTTP.sys delegates to kernel mode authentication with the Kerberos authentication protocol. User mode authentication isn't supported with Kerberos and HTTP.sys. The machine account must be used to decrypt the Kerberos token/ticket that's obtained from Active Directory and forwarded by the client to the server to authenticate the user. Register the Service Principal Name (SPN) for the host, not the user of the app.
How to use HTTP.sys
Configure the ASP.NET Core app to use HTTP.sys
Call the UseHttpSys extension method when building the host, specifying any required HttpSysOptions. The following example sets options to their default values:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseHttpSys(options =>
{
options.AllowSynchronousIO = false;
options.Authentication.Schemes = AuthenticationSchemes.None;
options.Authentication.AllowAnonymous = true;
options.MaxConnections = null;
options.MaxRequestBodySize = 30000000;
options.UrlPrefixes.Add("http://localhost:5005");
});
webBuilder.UseStartup<Startup>();
});
Additional HTTP.sys configuration is handled through registry settings.
HTTP.sys options
Property | Description | Default |
---|---|---|
AllowSynchronousIO | Control whether synchronous input/output is allowed for the HttpContext.Request.Body and HttpContext.Response.Body . |
false |
Authentication.AllowAnonymous | Allow anonymous requests. | true |
Authentication.Schemes | Specify the allowed authentication schemes. May be modified at any time prior to disposing the listener. Values are provided by the AuthenticationSchemes enum: Basic , Kerberos , Negotiate , None , and NTLM . |
None |
EnableResponseCaching | Attempt kernel-mode caching for responses with eligible headers. The response may not include Set-Cookie , Vary , or Pragma headers. It must include a Cache-Control header that's public and either a shared-max-age or max-age value, or an Expires header. |
true |
Http503Verbosity | The HTTP.sys behavior when rejecting requests due to throttling conditions. | Http503VerbosityLevel. Basic |
MaxAccepts | The maximum number of concurrent accepts. | 5 × Environment. ProcessorCount |
MaxConnections | The maximum number of concurrent connections to accept. Use -1 for infinite. Use null to use the registry's machine-wide setting. |
null (machine-wide setting) |
MaxRequestBodySize | See the MaxRequestBodySize section. | 30000000 bytes (~28.6 MB) |
RequestQueueLimit | The maximum number of requests that can be queued. | 1000 |
RequestQueueMode |
This indicates whether the server is responsible for creating and configuring the request queue, or if it should attach to an existing queue. Most existing configuration options do not apply when attaching to an existing queue. |
RequestQueueMode.Create |
RequestQueueName |
The name of the HTTP.sys request queue. | null (Anonymous queue) |
ThrowWriteExceptions | Indicate if response body writes that fail due to client disconnects should throw exceptions or complete normally. | false (complete normally) |
Timeouts | Expose the HTTP.sys TimeoutManager configuration, which may also be configured in the registry. Follow the API links to learn more about each setting, including default values:
|
|
UrlPrefixes | Specify the UrlPrefixCollection to register with HTTP.sys. The most useful is UrlPrefixCollection.Add, which is used to add a prefix to the collection. These may be modified at any time prior to disposing the listener. |
MaxRequestBodySize
The maximum allowed size of any request body in bytes. When set to null
, the maximum request body size is unlimited. This limit has no effect on upgraded connections, which are always unlimited.
The recommended method to override the limit in an ASP.NET Core MVC app for a single IActionResult
is to use the RequestSizeLimitAttribute attribute on an action method:
[RequestSizeLimit(100000000)]
public IActionResult MyActionMethod()
An exception is thrown if the app attempts to configure the limit on a request after the app has started reading the request. An IsReadOnly
property can be used to indicate if the MaxRequestBodySize
property is in a read-only state, meaning it's too late to configure the limit.
If the app should override MaxRequestBodySize per-request, use the IHttpMaxRequestBodySizeFeature:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env,
ILogger<Startup> logger, IServer server)
{
app.Use(async (context, next) =>
{
context.Features.Get<IHttpMaxRequestBodySizeFeature>()
.MaxRequestBodySize = 10 * 1024;
var serverAddressesFeature =
app.ServerFeatures.Get<IServerAddressesFeature>();
var addresses = string.Join(", ", serverAddressesFeature?.Addresses);
logger.LogInformation("Addresses: {Addresses}", addresses);
await next.Invoke();
});
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
If using Visual Studio, make sure the app isn't configured to run IIS or IIS Express.
In Visual Studio, the default launch profile is for IIS Express. To run the project as a console app, manually change the selected profile, as shown in the following screen shot:
Configure Windows Server
Determine the ports to open for the app and use Windows Firewall or the New-NetFirewallRule PowerShell cmdlet to open firewall ports to allow traffic to reach HTTP.sys. In the following commands and app configuration, port 443 is used.
When deploying to an Azure VM, open the ports in the Network Security Group. In the following commands and app configuration, port 443 is used.
Obtain and install X.509 certificates, if required.
On Windows, create self-signed certificates using the New-SelfSignedCertificate PowerShell cmdlet. For an unsupported example, see UpdateIISExpressSSLForChrome.ps1.
Install either self-signed or CA-signed certificates in the server's Local Machine > Personal store.
If the app is a framework-dependent deployment, install .NET Core, .NET Framework, or both (if the app is a .NET Core app targeting the .NET Framework).
- .NET Core: If the app requires .NET Core, obtain and run the .NET Core Runtime installer from .NET Core Downloads. Don't install the full SDK on the server.
- .NET Framework: If the app requires .NET Framework, see the .NET Framework installation guide. Install the required .NET Framework. The installer for the latest .NET Framework is available from the .NET Core Downloads page.
If the app is a self-contained deployment, the app includes the runtime in its deployment. No framework installation is required on the server.
Configure URLs and ports in the app.
By default, ASP.NET Core binds to
http://localhost:5000
. To configure URL prefixes and ports, options include:- UseUrls
urls
command-line argumentASPNETCORE_URLS
environment variable- UrlPrefixes
The following code example shows how to use UrlPrefixes with the server's local IP address
10.0.0.4
on port 443:public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseHttpSys(options => { options.UrlPrefixes.Add("https://10.0.0.4:443"); }); webBuilder.UseStartup<Startup>(); });
An advantage of
UrlPrefixes
is that an error message is generated immediately for improperly formatted prefixes.The settings in
UrlPrefixes
overrideUseUrls
/urls
/ASPNETCORE_URLS
settings. Therefore, an advantage ofUseUrls
,urls
, and theASPNETCORE_URLS
environment variable is that it's easier to switch between Kestrel and HTTP.sys.HTTP.sys uses the HTTP Server API UrlPrefix string formats.
Warning
Top-level wildcard bindings (
http://*:80/
andhttp://+:80
) should not be used. Top-level wildcard bindings create app security vulnerabilities. This applies to both strong and weak wildcards. Use explicit host names or IP addresses rather than wildcards. Subdomain wildcard binding (for example,*.mysub.com
) isn't a security risk if you control the entire parent domain (as opposed to*.com
, which is vulnerable). For more information, see RFC 9110: Section 7.2: Host and :authority.Preregister URL prefixes on the server.
The built-in tool for configuring HTTP.sys is netsh.exe. netsh.exe is used to reserve URL prefixes and assign X.509 certificates. The tool requires administrator privileges.
Use the netsh.exe tool to register URLs for the app:
netsh http add urlacl url=<URL> user=<USER>
<URL>
: The fully qualified Uniform Resource Locator (URL). Don't use a wildcard binding. Use a valid hostname or local IP address. The URL must include a trailing slash.<USER>
: Specifies the user or user-group name.
In the following example, the local IP address of the server is
10.0.0.4
:netsh http add urlacl url=https://10.0.0.4:443/ user=Users
When a URL is registered, the tool responds with
URL reservation successfully added
.To delete a registered URL, use the
delete urlacl
command:netsh http delete urlacl url=<URL>
Register X.509 certificates on the server.
Use the netsh.exe tool to register certificates for the app:
netsh http add sslcert ipport=<IP>:<PORT> certhash=<THUMBPRINT> appid="{<GUID>}"
<IP>
: Specifies the local IP address for the binding. Don't use a wildcard binding. Use a valid IP address.<PORT>
: Specifies the port for the binding.<THUMBPRINT>
: The X.509 certificate thumbprint.<GUID>
: A developer-generated GUID to represent the app for informational purposes.
For reference purposes, store the GUID in the app as a package tag:
- In Visual Studio:
- Open the app's project properties by right-clicking on the app in Solution Explorer and selecting Properties.
- Select the Package tab.
- Enter the GUID that you created in the Tags field.
- When not using Visual Studio:
Open the app's project file.
Add a
<PackageTags>
property to a new or existing<PropertyGroup>
with the GUID that you created:<PropertyGroup> <PackageTags>9412ee86-c21b-4eb8-bd89-f650fbf44931</PackageTags> </PropertyGroup>
In the following example:
- The local IP address of the server is
10.0.0.4
. - An online random GUID generator provides the
appid
value.
netsh http add sslcert ipport=10.0.0.4:443 certhash=b66ee04419d4ee37464ab8785ff02449980eae10 appid="{9412ee86-c21b-4eb8-bd89-f650fbf44931}"
When a certificate is registered, the tool responds with
SSL Certificate successfully added
.To delete a certificate registration, use the
delete sslcert
command:netsh http delete sslcert ipport=<IP>:<PORT>
Reference documentation for netsh.exe:
Run the app.
Administrator privileges aren't required to run the app when binding to localhost using HTTP (not HTTPS) with a port number greater than 1024. For other configurations (for example, using a local IP address or binding to port 443), run the app with administrator privileges.
The app responds at the server's public IP address. In this example, the server is reached from the Internet at its public IP address of
104.214.79.47
.A development certificate is used in this example. The page loads securely after bypassing the browser's untrusted certificate warning.
Proxy server and load balancer scenarios
For apps hosted by HTTP.sys that interact with requests from the Internet or a corporate network, additional configuration might be required when hosting behind proxy servers and load balancers. For more information, see Configure ASP.NET Core to work with proxy servers and load balancers.
Advanced HTTP/2 features to support gRPC
Additional HTTP/2 features in HTTP.sys support gRPC, including support for response trailers and sending reset frames.
Requirements to run gRPC with HTTP.sys:
- Windows 10, OS Build 19041.508 or later
- TLS 1.2 or later connection
Trailers
HTTP Trailers are similar to HTTP Headers, except they are sent after the response body is sent. For IIS and HTTP.sys, only HTTP/2 response trailers are supported.
if (httpContext.Response.SupportsTrailers())
{
httpContext.Response.DeclareTrailer("trailername");
// Write body
httpContext.Response.WriteAsync("Hello world");
httpContext.Response.AppendTrailer("trailername", "TrailerValue");
}
In the preceding example code:
SupportsTrailers
ensures that trailers are supported for the response.DeclareTrailer
adds the given trailer name to theTrailer
response header. Declaring a response's trailers is optional, but recommended. IfDeclareTrailer
is called, it must be before the response headers are sent.AppendTrailer
appends the trailer.
Reset
Reset allows for the server to reset a HTTP/2 request with a specified error code. A reset request is considered aborted.
var resetFeature = httpContext.Features.Get<IHttpResetFeature>();
resetFeature.Reset(errorCode: 2);
Reset
in the preceding code example specifies the INTERNAL_ERROR
error code. For more information about HTTP/2 error codes, visit the HTTP/2 specification error code section.
Additional resources
ASP.NET Core
Feedback
https://aka.ms/ContentUserFeedback.
Coming soon: Throughout 2024 we will be phasing out GitHub Issues as the feedback mechanism for content and replacing it with a new feedback system. For more information see:Submit and view feedback for