Can HttpClient (C#) be used with HTTP/2?

Yes, HttpClient in C# fully supports HTTP/2 starting with .NET Core 2.1. HTTP/2 provides significant performance benefits including multiplexing, header compression, and binary framing. However, both client and server must support HTTP/2 for it to work effectively.

Requirements

  • .NET Core 2.1 or later (including .NET 5, 6, 7, 8+)
  • HTTPS connection (HTTP/2 requires TLS in most implementations)
  • Server support for HTTP/2 protocol

Basic HTTP/2 Usage

Method 1: Explicit Version Setting

using System;
using System.Net.Http;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        using var client = new HttpClient();

        var request = new HttpRequestMessage()
        {
            RequestUri = new Uri("https://httpbin.org/get"),
            Method = HttpMethod.Get,
            Version = HttpVersion.Version20 // Force HTTP/2
        };

        try
        {
            var response = await client.SendAsync(request);

            Console.WriteLine($"Protocol: HTTP/{response.Version}");
            Console.WriteLine($"Status: {response.StatusCode}");

            var content = await response.Content.ReadAsStringAsync();
            Console.WriteLine($"Response: {content}");
        }
        catch (HttpRequestException ex)
        {
            Console.WriteLine($"Request failed: {ex.Message}");
        }
    }
}

Method 2: Configure Default Version

using var client = new HttpClient();
client.DefaultRequestVersion = HttpVersion.Version20;
client.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionExact;

// All requests will now use HTTP/2
var response = await client.GetAsync("https://httpbin.org/get");
Console.WriteLine($"Protocol: HTTP/{response.Version}");

Version Policies

Control how HttpClient handles HTTP version negotiation:

using var client = new HttpClient();

// Always use exact version, no fallback
client.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionExact;

// Use requested version or higher
client.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher;

// Use requested version or lower (allows fallback)
client.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrLower;

Multiple Concurrent Requests

HTTP/2's multiplexing allows multiple requests over a single connection:

using var client = new HttpClient();
client.DefaultRequestVersion = HttpVersion.Version20;

var tasks = new List<Task<HttpResponseMessage>>();

// Send 10 concurrent requests - HTTP/2 will multiplex them
for (int i = 0; i < 10; i++)
{
    var task = client.GetAsync($"https://httpbin.org/delay/{i}");
    tasks.Add(task);
}

var responses = await Task.WhenAll(tasks);

foreach (var response in responses)
{
    Console.WriteLine($"Status: {response.StatusCode}, Protocol: HTTP/{response.Version}");
    response.Dispose();
}

Checking HTTP/2 Support

Verify if a server supports HTTP/2:

public static async Task<bool> SupportsHttp2(string url)
{
    using var client = new HttpClient();

    var request = new HttpRequestMessage(HttpMethod.Head, url)
    {
        Version = HttpVersion.Version20,
        VersionPolicy = HttpVersionPolicy.RequestVersionExact
    };

    try
    {
        var response = await client.SendAsync(request);
        return response.Version.Major == 2;
    }
    catch
    {
        return false;
    }
}

// Usage
bool supportsHttp2 = await SupportsHttp2("https://google.com");
Console.WriteLine($"Server supports HTTP/2: {supportsHttp2}");

Server Configuration (ASP.NET Core)

Kestrel Configuration

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.ConfigureKestrel(options =>
            {
                options.ListenAnyIP(5001, listenOptions =>
                {
                    listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
                    listenOptions.UseHttps();
                });

                // HTTP/2 only (no HTTP/1.1 fallback)
                options.ListenAnyIP(5002, listenOptions =>
                {
                    listenOptions.Protocols = HttpProtocols.Http2;
                    listenOptions.UseHttps();
                });
            });
        });

appsettings.json Configuration

{
  "Kestrel": {
    "EndpointDefaults": {
      "Protocols": "Http1AndHttp2"
    },
    "Endpoints": {
      "Https": {
        "Url": "https://localhost:5001",
        "Protocols": "Http1AndHttp2"
      },
      "Http2Only": {
        "Url": "https://localhost:5002",
        "Protocols": "Http2"
      }
    }
  }
}

Performance Considerations

Connection Pooling

HTTP/2 uses fewer connections due to multiplexing:

// Configure connection limits for HTTP/2
var handler = new SocketsHttpHandler()
{
    PooledConnectionLifetime = TimeSpan.FromMinutes(2),
    MaxConnectionsPerServer = 2 // Fewer connections needed with HTTP/2
};

using var client = new HttpClient(handler);
client.DefaultRequestVersion = HttpVersion.Version20;

Stream Limits

Be aware of HTTP/2 stream limits:

// Monitor concurrent streams
var semaphore = new SemaphoreSlim(100, 100); // Limit concurrent requests

var tasks = urls.Select(async url =>
{
    await semaphore.WaitAsync();
    try
    {
        return await client.GetAsync(url);
    }
    finally
    {
        semaphore.Release();
    }
});

Common Issues and Solutions

1. HTTPS Requirement

// HTTP/2 typically requires HTTPS
var handler = new HttpClientHandler()
{
    ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true
};

using var client = new HttpClient(handler);

2. Version Fallback Handling

public static async Task<HttpResponseMessage> GetWithFallback(string url)
{
    using var client = new HttpClient();

    // Try HTTP/2 first
    var request = new HttpRequestMessage(HttpMethod.Get, url)
    {
        Version = HttpVersion.Version20,
        VersionPolicy = HttpVersionPolicy.RequestVersionOrLower
    };

    var response = await client.SendAsync(request);

    if (response.Version.Major < 2)
    {
        Console.WriteLine("Fell back to HTTP/1.1");
    }

    return response;
}

Limitations

  • Server Push: Not supported by HttpClient
  • Trailers: Limited support for HTTP/2 trailers
  • Frame Types: Only standard HTTP/2 frames are supported
  • ALPN: Automatic, but limited configuration options

Best Practices

  1. Reuse HttpClient instances to benefit from connection pooling
  2. Use async/await to maximize HTTP/2 multiplexing benefits
  3. Set appropriate timeouts for long-lived connections
  4. Monitor connection health in production environments
  5. Test fallback scenarios when HTTP/2 is unavailable

HTTP/2 support in HttpClient provides significant performance improvements for modern web applications, especially when making multiple concurrent requests to the same server.

Related Questions

Get Started Now

WebScraping.AI provides rotating proxies, Chromium rendering and built-in HTML parser for web scraping
Icon