Web protocols comparison

HTTP/1.1 vs HTTP/2

HTTP/1.1 has powered the web since 1997. HTTP/2, standardised in RFC 7540 in 2015, was engineered to fix its fundamental performance bottlenecks — multiplexing, header compression, and binary framing — without changing the HTTP semantics developers already know. Understanding the difference is essential vocabulary for any web performance or backend architecture conversation.

TL;DR

  • HTTP/1.1 — text-based, one request per connection at a time, head-of-line blocking, verbose headers on every request. Workarounds include domain sharding, resource bundling, and CSS sprites.
  • HTTP/2 — binary framing, full multiplexing over one TCP connection, HPACK header compression, optional server push, stream prioritisation. Requires HTTPS in browsers. No application code changes needed.
  • Recommendation: enable HTTP/2 everywhere. Browser support is universal. The upgrade is a server configuration change. Performance improvements are meaningful, especially for pages with many assets.

Side-by-side comparison

AspectHTTP/1.1HTTP/2
Framing Text-based — human-readable headers and body separated by \r\n Binary frames — fixed structure, more efficient to parse
Multiplexing One request at a time per TCP connection (pipelining rarely used) Multiple streams concurrently over one TCP connection
Head-of-line blocking Yes — a slow request blocks the queue on that connection Eliminated at HTTP layer (TCP-level HOL blocking remains)
Header compression Plain text on every request — typically 500–2000 bytes overhead HPACK — shared dynamic table, indexes replace repeated headers
Server push Not supported Supported per spec; largely deprecated in favour of 103 Early Hints
Request prioritisation Not supported — all requests are equal Stream weights and dependencies hint the server on delivery order
TCP connections per origin Browsers open 6–8 parallel connections to work around HOL blocking One connection per origin is sufficient
TLS / HTTPS Optional Required by all browsers in practice (optional per RFC)
Application code changes None — transparent upgrade at the transport layer
Browser support Universal 97%+ (effectively universal)

What is HTTP/1.1?

HTTP/1.1 (RFC 2616, 1999; revised in RFC 7230–7235, 2014) is the text-based request–response protocol that defined the web for nearly two decades. Each request is a human-readable ASCII message containing a method line, headers, and an optional body. Responses follow the same structure.

The core limitation is connection throughput: a single TCP connection handles one request–response exchange at a time. Once you send a GET request, you must wait for the full response before sending the next. Browsers compensate by opening multiple TCP connections in parallel — typically six per domain — but this multiplies the overhead of TCP and TLS handshakes and consumes server resources proportionally.

The performance workarounds HTTP/1.1 forced on developers — bundling many JavaScript files into one, inlining images as base64, using CSS sprites, splitting assets across multiple subdomains (domain sharding) — are largely unnecessary with HTTP/2.

What is HTTP/2?

HTTP/2 (RFC 7540, 2015) was developed by the IETF HTTP Working Group, building on Google's SPDY protocol. It replaces HTTP/1.1's text framing with a binary framing layer — every communication is split into small, typed frames (HEADERS, DATA, SETTINGS, PUSH_PROMISE, and others) with a fixed binary structure.

Above the framing layer, HTTP/2 introduces streams: independent, bidirectional sequences of frames within a single TCP connection. Each stream carries one request–response pair and is identified by an integer stream ID. Because streams are multiplexed on one connection, dozens of requests can be in flight simultaneously without the overhead of separate TCP connections.

HTTP/2 is a drop-in replacement at the protocol level. The HTTP methods (GET, POST, PUT…), status codes (200, 404, 500…), and header semantics are unchanged. Only the wire format differs.

Key concepts explained

Multiplexing and head-of-line blocking

In HTTP/1.1, if a browser has six requests queued and opens six TCP connections, each connection is still blocked until its in-flight request completes. A slow API call on connection 3 does not affect connection 5, but connection 3 is entirely stalled for that request. HTTP pipelining — sending multiple requests without waiting for responses — was specified in HTTP/1.1 but disabled in most browsers due to implementation difficulties.

HTTP/2 multiplexing solves this cleanly. A single connection carries many streams; a DATA frame for stream 7 (a large image) can be interleaved with HEADERS frames for stream 9 (an API call). Neither blocks the other at the HTTP layer. Note: TCP-level head-of-line blocking still applies — if a TCP segment is lost, all HTTP/2 streams on that connection stall waiting for retransmission. This residual problem is why HTTP/3 moved to QUIC over UDP.

Binary framing

HTTP/1.1 messages are text: the parser must scan for \r\n line endings and specific string delimiters. This is simple to read and debug but inefficient to parse at scale. HTTP/2 frames have a fixed 9-byte header: a 3-byte length, 1-byte type, 1-byte flags, and a 4-byte stream identifier. Parsing is a matter of reading fixed-width fields, not scanning variable-length text.

HPACK header compression

HTTP headers contain a great deal of repeated information: the Host, Cookie, Accept-Encoding, User-Agent, and Authorization headers are typically identical on every request to the same origin. In HTTP/1.1, these are retransmitted in full as plain text on every request.

HPACK maintains a dynamic header table shared between client and server, pre-seeded with a static table of 61 common header name/value pairs. When a header has been seen before, it is encoded as a small integer index. New header values are added to the dynamic table and referenced thereafter. This can reduce header overhead by 85–90% on typical workloads.

Request prioritisation

HTTP/2 allows streams to carry priority information: a weight (1–256) and a dependency on another stream. A browser can signal that the CSS stream should be delivered before the JavaScript stream, which should complete before image streams. Servers are not required to honour priorities, but well-implemented servers (Nginx, H2O, nghttp2) use them to improve page render times.

Server push (and its decline)

HTTP/2 server push allowed a server responding to a request for index.html to proactively send style.css and app.js via PUSH_PROMISE frames before the browser had parsed the HTML and discovered it needed them. The idea was sound, but practical use proved difficult: servers had no reliable way to know which resources were already in the browser cache, leading to redundant data transfer. Chrome removed push support in 2022. The HTTP 103 Early Hints status code — where the server sends a preliminary response with Link: rel=preload hints — is now the preferred mechanism.

HTTP/3 context

HTTP/3 (RFC 9114, 2022) addresses the one performance problem HTTP/2 could not: TCP-level head-of-line blocking. It runs over QUIC, a transport protocol built on UDP that implements reliable, ordered, multiplexed streams natively, with per-stream loss recovery. A lost UDP packet only stalls the stream it belongs to, not all streams on the connection. QUIC also enables faster connection setup (0-RTT resumption) and better performance on lossy mobile networks. HTTP/3 is complementary to HTTP/2 — both are active improvements on HTTP/1.1 and you may serve both depending on client capability.

How engineers talk about HTTP/1.1 vs HTTP/2

These phrases appear regularly in code reviews, architecture discussions, and performance debugging sessions.

HTTP/1.1 context

  • "We're using domain sharding — splitting assets across cdn1, cdn2, cdn3 — to open more parallel TCP connections."
  • "The waterfall shows request queuing — six connections are all busy so the next batch has to wait."
  • "We bundle all our JS into one file to keep the request count down."
  • "Head-of-line blocking is killing performance on the mobile network — every slow request stalls the queue."

HTTP/2 context

  • "With HTTP/2 multiplexing, all our JS modules load in parallel over one connection — no more bundling required."
  • "HPACK cuts our header overhead significantly — our cookie is 1.2 KB and used to be repeated on every request."
  • "We enabled http2 on Nginx — it's just one word on the listen directive."
  • "The waterfall shows all requests starting simultaneously — that's multiplexing working correctly."
  • "We're using stream prioritisation to ensure the above-the-fold CSS arrives before hero images."

Cross-version discussions

  • "We're still seeing TCP-level HOL blocking on bad connections — we're evaluating HTTP/3 / QUIC as the next step."
  • "HTTP/2 is a wire-level change only — the application sees the same request objects as before."
  • "103 Early Hints replaced server push for us — it achieves the same goal without the cache invalidation headaches."

Enabling HTTP/2 in Nginx

The change is minimal — add http2 to the listen directive (Nginx 1.25.1+ syntax):

server {
    listen 443 ssl;
    http2 on;

    ssl_certificate     /etc/ssl/certs/example.crt;
    ssl_certificate_key /etc/ssl/private/example.key;

    server_name example.com;
    root /var/www/html;
}

For older Nginx versions (before 1.25.1), the syntax was listen 443 ssl http2;. No application changes are required.

Decision guide

  • New public-facing web service → Enable HTTP/2 by default
  • Already on HTTPS → Enable HTTP/2 immediately (server config only)
  • Page loads many small assets (JS modules, CSS, fonts, images) → HTTP/2 multiplexing gives measurable improvement
  • Internal microservice-to-microservice communication → Consider h2c (HTTP/2 cleartext) for lower overhead without TLS
  • Users on lossy mobile networks → Investigate HTTP/3 / QUIC as a further step
  • Plain HTTP internal tool with no browser clients → HTTP/1.1 is acceptable
  • Considering reverting HTTP/1.1 performance hacks (bundling, sprites, sharding) → Safe to remove once HTTP/2 is confirmed active

Key vocabulary

Head-of-line (HOL) blocking
When a slow or stalled item at the front of a queue prevents all items behind it from being processed. In HTTP/1.1 it occurs at the request level; in HTTP/2 it can still occur at the TCP level on packet loss.
Multiplexing
Sending multiple independent streams of data over a single connection simultaneously, each identified by a stream ID, and interleaving their frames.
HPACK
HTTP/2's header compression format (RFC 7541). Uses a static table of 61 common headers and a dynamic table built at runtime to encode repeated headers as compact integers.
Binary framing layer
HTTP/2's lowest layer — all communication is split into typed frames (HEADERS, DATA, SETTINGS, PUSH_PROMISE, RST_STREAM, WINDOW_UPDATE…) with a fixed 9-byte binary header.
Stream
An independent, bidirectional sequence of HTTP/2 frames within a single TCP connection. Each stream carries one request–response exchange and has a unique integer identifier.
ALPN (Application-Layer Protocol Negotiation)
A TLS extension that allows the client and server to agree on HTTP/2 or HTTP/1.1 during the TLS handshake, avoiding an additional round trip.
QUIC
A UDP-based transport protocol (RFC 9000) used by HTTP/3. Implements multiplexed reliable streams with per-stream loss recovery, eliminating TCP-level head-of-line blocking.
103 Early Hints
An HTTP status code allowing the server to send a preliminary response with Link: rel=preload headers before the final response is ready, letting browsers begin fetching critical resources early — the modern replacement for HTTP/2 server push.

Frequently asked questions

What is head-of-line blocking in HTTP/1.1?

Head-of-line (HOL) blocking occurs when a slow or stalled request prevents all subsequent requests on the same TCP connection from being processed. HTTP/1.1 handles one request at a time per connection. Browsers work around this by opening 6–8 parallel TCP connections per domain, but this is wasteful and still limited. HTTP/2 eliminates HOL blocking at the HTTP layer through multiplexing — though TCP-level HOL blocking remains, which HTTP/3 addresses.

What is HTTP/2 multiplexing?

Multiplexing allows multiple HTTP/2 requests and responses to be in flight simultaneously over a single TCP connection. Each request is split into binary frames tagged with a stream ID. The server interleaves frames from different streams and the client reassembles them. This means a slow database response for one request does not delay the CSS file loading in another — both travel concurrently on the same connection.

What is HPACK and why does it matter?

HPACK is the header compression algorithm used by HTTP/2. HTTP/1.1 sends all headers as plain text on every single request — including large cookies, User-Agent strings, and Accept headers, often amounting to 500–2000 bytes per request. HPACK maintains a shared dynamic table of recently seen header name/value pairs on both client and server. Repeated headers are sent as compact integer indexes rather than full strings, substantially reducing overhead on sites with many requests.