// Engineering Log

HTTP, HTTPS, and TLS: Part 3 — HTTP/1.1, HTTP/2, and HTTP/3: how the versions differ

Published on 2026-09-30

// Fast route

This article belongs to the topic Servers and infrastructure.

HTTP has three versions in active use: HTTP/1.1, HTTP/2, and HTTP/3. Methods, headers, and status codes are the same across them, so an application usually doesn’t care which version a request arrived with. The difference is how messages are framed and transmitted over the network, and this determines page load speed with dozens of resources and behavior on poor mobile connections.

HTTP/1.1

The 1997 version (now — RFC 9112). Messages are text, and the TCP connection is kept open between requests (keep-alive).

The main limitation is queuing. You cannot send a second request on a single connection until the response to the first arrives. The pipelining mechanism that allowed sending several requests in a row existed in the standard, but responses still had to come in order, and one slow image would delay everything else. Browsers never enabled it.

Workarounds from the HTTP/1.1 era:

  • up to six connections to one host in the browser;
  • domain sharding — serving static files from static1.example.ru, static2.example.ru to get more connections;
  • concatenating scripts and styles into a single file, image sprites.

Each new connection is a TCP and TLS handshake, a separate TCP slow start, and extra load on the server.

HTTP/2

The 2015 standard (now — RFC 9113), grown out of Google’s SPDY protocol.

Binary frames instead of text. A message is split into frames: a header frame, data frames. Each frame is marked with a stream identifier.

Multiplexing. Dozens of streams run concurrently over one TCP connection, one per request. Frames from different responses are interleaved, and a slow response does not hold others. One connection per site is enough for the browser, and domain sharding and file concatenation become unnecessary and even harmful: multiple domains mean multiple connections and DNS lookups.

HPACK header compression. In HTTP/1.1 each request retransmits User-Agent, Cookie, Accept — hundreds of bytes repeated request to request. HPACK keeps a table of previously sent headers on both sides and sends only references to it.

Pseudo-headers. The start line is replaced by fields :method, :path, :scheme, :authority (instead of Host) in requests and :status in responses. All header names are lowercase.

Server push — the server sends resources the client hasn’t yet asked for. In practice it rarely saved anything, and Chrome disabled it in version 106 (2022). Instead of push people use the 103 Early Hints response and <link rel="preload">.

TLS only in browsers. The standard allows HTTP/2 without encryption (h2c), but no browser supports it. The version is negotiated during the TLS handshake via the ALPN extension: the client offers h2, http/1.1, the server chooses. h2c is seen in internal networks, for example in gRPC between services.

A remaining problem. Multiplexing removed queuing at the HTTP level, but not at the TCP level. TCP delivers bytes strictly in order, and if one packet is lost, all streams in the connection wait for its retransmission, even those whose data wasn’t in it. This is called head-of-line blocking. On a stable channel it’s unnoticeable, but with 1–2% losses on mobile networks HTTP/2 over a single connection can perform worse than HTTP/1.1 with six connections.

HTTP/3 and QUIC

HTTP/3 (RFC 9114, 2022) runs over QUIC (RFC 9000) — a transport protocol on UDP that took over tasks of TCP and TLS.

Transport-level streams. QUIC is aware of streams itself, and a lost packet delays only the stream whose data was in it. Head-of-line blocking disappears.

TLS 1.3 is built-in. Encryption is mandatory in QUIC; there is no separate TLS handshake over the transport — keys are agreed in the first QUIC packets. A new connection is established in one round trip (1-RTT) instead of two for TCP + TLS 1.3; a resumed connection can send data immediately (0-RTT). Transport-level fields that are visible to everyone in TCP — packet numbers, acknowledgements — are encrypted too.

Connection migration. A QUIC connection is identified not by an IP/port pair but by a connection ID. When a phone switches from Wi-Fi to mobile, downloads continue without a new handshake.

QPACK header compression — a variant of HPACK adapted to out-of-order streams.

How the browser learns about HTTP/3. The first connection to a site goes over TCP (HTTP/2 or 1.1). The server advertises HTTP/3 support with a header:

alt-svc: h3=":443"; ma=86400

and the browser will try QUIC on UDP port 443 for subsequent requests. The other way is an HTTPS DNS record with the parameter alpn="h3": then the browser may go to HTTP/3 immediately.

Limitations. UDP/443 is closed in some corporate networks, and in some networks QUIC is blocked or deliberately slowed. Browsers in that case silently fall back to TCP, so HTTP/3 is always enabled in addition to HTTP/2, not instead of it. Implementing QUIC in user space consumes more CPU on the server than TCP in the kernel.

Summary table

HTTP/1.1HTTP/2HTTP/3
TransportTCPTCPQUIC (UDP)
Formattextbinary framesbinary frames
Requests per connection concurrently1manymany
Header compressionnoneHPACKQPACK
Encryptionoptionalmandatory in browsersalways, TLS 1.3
Packet loss delaysone connectionall streams in the connectiononly the affected stream
Version negotiationby defaultALPN h2Alt-Svc or DNS HTTPS, ALPN h3

Enabling in Nginx

HTTP/2 in Nginx 1.25.1 and newer is enabled with a separate directive (the old form listen 443 ssl http2 is deprecated). HTTP/3 is via listen ... quic; the ngx_http_v3_module is included in official packages from nginx.org starting with 1.25:

nginx
server {
    listen 443 ssl;
    listen 443 quic reuseport;   # reuseport — only in one server block per port
    listen [::]:443 ssl;
    listen [::]:443 quic reuseport;

    http2 on;
    http3 on;

    server_name example.ru;
    ssl_certificate     /etc/letsencrypt/live/example.ru/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.ru/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;   # TLSv1.3 is required for QUIC

    add_header Alt-Svc 'h3=":443"; ma=86400' always;
}

Don’t forget to open UDP/443 in the firewall — this is the most common reason HTTP/3 “doesn’t enable”:

bash
ufw allow 443/udp

Caddy enables HTTP/2 and HTTP/3 automatically, without configuration. HAProxy has HTTP/3 since version 2.6 (bind quic4@:443 ssl crt ... alpn h3).

How to check the version

curl shows the negotiated version in the -v output and in the http_version variable:

bash
curl -sI --http2 https://example.ru/ -o /dev/null -w '%{http_version}\n'
# 2

curl -sI --http3 https://example.ru/ -o /dev/null -w '%{http_version}\n'
# 3 — if curl is built with HTTP/3 support

Whether your curl supports HTTP/3 is visible in the Features line of curl --version output (the word HTTP3). The system curl in macOS and many distributions is built without it. You can test with a Docker image where curl is built with HTTP/3, or via a browser: in developer tools on the Network tab enable the Protocol column — it will show h2 or h3.

The --http3 flag tries QUIC but also starts a parallel TCP attempt and falls back to it if QUIC doesn’t respond. To test QUIC only without fallback, use --http3-only.

Common mistakes

  • HTTP/3 is enabled in the config, but UDP/443 is closed on the server or by the hoster — browsers silently stay on HTTP/2.
  • reuseport is specified in multiple server blocks for the same address — Nginx won’t start with an error about a duplicated parameter.
  • Internal services behind a proxy expect HTTP/2 (gRPC), but the proxy connects to them over HTTP/1.1 — in Nginx you need grpc_pass, not proxy_pass.
  • Concatenating all scripts into one huge file out of habit from HTTP/1.1 — with HTTP/2 small files load in parallel and are better cached separately.

// Similar task

If you are dealing with something similar

This article belongs to one of the main working topics. You can keep reading on the topic, go to the homepage to understand what I do, or open the service pages directly.

Article topic

Servers and infrastructure

VPS, Linux, web stack, migrations, hosting, databases, and core operations.

Typical tasks behind this topic

  • Move a site or service to a new server
  • Set up Linux, Nginx, databases, and backups
  • Figure out why the system behaves unstably

// Next step

If you need help with this topic, not just another article, it is better to go straight to the service page. The homepage and topic collection stay available as secondary routes.

Open services

// Reviews

Related reviews

apande

apande

Configuring Nginx and OpenCart

2024-09-07 · ★ 5/5

A very powerful buyer
kireevk

kireevk

Diagnosing Nginx Proxy Manager in a Docker container and resolving the issue

2024-03-15 · ★ 5/5

Settled-in customer

// Contact

Need help?

Get in touch with me and I'll help solve the problem

I reply within one business day (03:00-13:00 GMT)

Или оставьте заявку здесь:

Confirm that you are not a bot.

Write and get a quick reply