How do I (and is it possible) to enable http2 over plain http in the C# Kestrel web server? All Microsoft documentation indicates that https/TLS is required, but I have services that will be running behind a load-balancer or nginx and as such don't need a second layer of https. The official http2 spec indicates that https is not required.
Scenarios to use unencrypted http2 are load balancers, proxies, etc.
You must do three things to use http2 over unencrypted channel.
Setup Kestrel to use http2 on your server:
builder.ConfigureWebHostDefaults((webBuilder) =>
{
// this will keep your other end points settings such as --urls parameter
webBuilder.ConfigureKestrel((options) =>
{
// trying to use Http1AndHttp2 causes http2 connections to fail with invalid protocol error
// according to Microsoft dual http version mode not supported in unencrypted scenario: https://docs.microsoft.com/en-us/aspnet/core/grpc/troubleshoot?view=aspnetcore-3.0
options.ConfigureEndpointDefaults(lo => lo.Protocols = HttpProtocols.Http2);
});
});
For .net 5+, create your HttpClient
instance, then create a message and specify the version:
var request = new HttpRequestMessage(HttpMethod.Get, uri)
{
Version = HttpVersion.Version20,
VersionPolicy = HttpVersionPolicy.RequestVersionOrHigher
};
For .net core 3.1 and older, set a flag to enable http2 unencrypted. Then, when you create an HttpClient
, specify the version:
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
var client = new HttpClient { BaseAddress = new Uri(baseUrl), DefaultRequestVersion = new Version(2, 0) };
If you need to support both http1 and http2 on a completely unencrypted host, then you will need to listen on two ports, one for each http version. Then your load balancer or proxy would need to handle the http version and direct to the appropriate port.
You won't see http2 on your browser and will likely get a protocol error, so in those cases you can use an http1 protocol directive just for development environment. Not ideal, but it at least lets you test locally.
Make sure you are using Microsoft.AspNetCore.WebHost
instead of the generic Microsoft.Extensions.Hosting.Host
for the CreateDefaultBuilder
as you will run into an annoying OpenSslCryptographicException
when running dotnet core 3 grpc containers on docker linux instances.
Also, if you are using health checks, be sure to expose it on a different port as illustrated in the below Program.cs
snippet:
public static IWebHostBuilder CreateHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureKestrel(options => {
//grpc port for http2 connections
options.ListenAnyIP(xxxx, listenOptions => {
listenOptions.Protocols = HttpProtocols.Http2;
});
//add health check on a separate port instead of Http1AndHttp2 on same port
options.ListenAnyIP(xxxx, listenOptions => {
listenOptions.Protocols = HttpProtocols.Http1;
});
})
.UseStartup<Startup>();
The easiest way is to use configuration section for Kestrel in appSettings.json.
"Kestrel": {
"EndpointDefaults": {
"Protocols": "Http2"
}
},
To turn of SSL, you may want to remove app.UseHsts()
, app.UseHttpsRedirections()
options from Statup.cs Configure method and expose only Http url.
You can also use Http1AndHttp2
to support both Http1
and Http2
together.
In dotnet 5 AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
is no longer supported.
To allow prior knowledge http/2 you need to configure your HttpRequestMessage:
var request = new HttpRequestMessage(HttpMethod.Get, new Uri("http://localhost/something"))
{
Version = HttpVersion.Version20,
VersionPolicy = HttpVersionPolicy.RequestVersionOrHigher //This allows http/2 unencrypted,
};
For reference: https://github.com/dotnet/runtime/issues/987
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With