HTTP 2

Intro

HTTP/2 is the second major version of the HTTP protocol, standardized in 2015 (RFC 7540, superseded by RFC 9113). It runs over a single TCP connection and multiplexes many request/response pairs simultaneously, eliminating the head-of-line blocking and connection overhead that limited HTTP/1.1. The result is lower latency and higher throughput for web applications that make many concurrent requests — without changing the HTTP semantics (methods, headers, status codes) that applications already use.

See HTTP for the foundational HTTP concepts that HTTP/2 builds on.

What HTTP/1.1 Got Wrong

HTTP/1.1 has two fundamental performance problems:

  1. Head-of-line blocking at the application layer: requests on a single connection are processed sequentially. If request 1 is slow, requests 2 and 3 wait. Browsers work around this by opening 6–8 parallel TCP connections per origin — wasteful and limited.

  2. Verbose headers: HTTP/1.1 headers are plain text and sent in full with every request. A typical request has 500–800 bytes of headers. For small API calls, headers can be larger than the payload.

How HTTP/2 Works

Binary framing layer
HTTP/2 replaces the text-based HTTP/1.1 format with a binary framing layer. Messages are split into frames (the smallest unit of communication), each tagged with a stream ID.

Multiplexing
Multiple streams share a single TCP connection. Frames from different streams are interleaved. Stream 1's response frames can arrive between stream 3's request frames. No waiting.

flowchart TD
  A[One TCP connection] --> B[Stream 1: GET /api/users]
  A --> C[Stream 3: GET /api/orders]
  A --> D[Stream 5: POST /api/events]
  B --> E[Frames interleaved on the wire]
  C --> E
  D --> E

HPACK header compression
Headers are compressed using a static table (common headers like :method: GET) and a dynamic table (headers seen in previous requests on the same connection). A repeated Authorization header costs ~1 byte instead of ~500 bytes.

Server push
The server can proactively send resources the client will need before the client requests them. For example, when serving index.html, the server can push style.css and app.js without waiting for the browser to parse the HTML and request them.

In practice, server push has limited adoption — it is hard to predict what the client needs, and browsers often already have resources cached. HTTP/3 has deprecated server push.

Stream prioritization
Clients can assign priorities to streams, allowing the server to allocate bandwidth accordingly (e.g., prioritize HTML over images). In practice, priority support varies across implementations.

HTTP/2 vs HTTP/1.1

Feature HTTP/1.1 HTTP/2
Connections per origin 6–8 (browser workaround) 1
Request multiplexing No (sequential per connection) Yes
Header format Plain text, repeated in full Binary, HPACK compressed
Server push No Yes (limited adoption)
Head-of-line blocking Application layer TCP layer only
TLS requirement Optional Required in practice (browsers enforce)

HTTP/2 in .NET

ASP.NET Core supports HTTP/2 natively via Kestrel. Enable it in appsettings.json or Program.cs:

builder.WebHost.ConfigureKestrel(options =>
{
    options.ListenAnyIP(443, listenOptions =>
    {
        listenOptions.UseHttps();
        listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
    });
});

HttpClient in .NET 5+ uses HTTP/2 by default when the server supports it:

var client = new HttpClient
{
    DefaultRequestVersion = HttpVersion.Version20,
    DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher
};

Pitfalls

TCP head-of-line blocking remains
HTTP/2 eliminates application-layer head-of-line blocking but not TCP-layer blocking. A single lost packet stalls all streams on the connection until TCP retransmits it. Under high packet loss (mobile networks, congested links), HTTP/2 can perform worse than HTTP/1.1 with multiple connections. HTTP/3 (QUIC) solves this by using UDP with per-stream loss recovery.

Single connection amplifies TCP congestion
HTTP/1.1 uses multiple connections, so congestion on one does not affect others. HTTP/2's single connection means a congestion event affects all streams simultaneously.

Server push cache invalidation
Pushed resources may already be in the client's cache. The server has no way to know, so it wastes bandwidth pushing resources the client doesn't need. Most production deployments disable server push.

Questions

References


Whats next