Guide · Backend
HTTP/1.1 vs HTTP/2 vs HTTP/3: What Each Version Fixed
23 min readUpdated By Saket Bhatnagar
Researched and drafted with AI, reviewed and edited by Saket Bhatnagar.
In short
- The http2 vs http3 difference is the transport underneath. HTTP/2 runs over TCP, the internet's reliable in-order delivery layer. HTTP/3 runs over QUIC, a newer transport built on UDP.
- HTTP/1.1 keeps a connection open for many requests, but each connection carries one request and reply at a time. So browsers commonly open up to six connections per host.
- HTTP/2 cuts messages into small labelled pieces called frames and mixes many requests on one connection. Yet one lost packet pauses all of them, because TCP hands over bytes strictly in order.
- HTTP/3 fixes that pause: a lost packet holds up only the requests whose data it carried. New connections also start one round trip (one message there and back) sooner.
- Beginners can stop after the section on which version to use. Engineers then get a runnable packet-loss model, measured header sizes, where QPACK can still block, and a tested nginx 1.28 HTTP/3 setup.
What goes wrong when a page with 40 small images loads on mobile data?
The page spends most of its time waiting, not downloading. Every image needs its own request and reply, and the HTTP version decides how much of that waiting happens in a queue. On lossy networks, the http2 vs http3 choice decides most of it.
Picture a common setup. A comics reading app shows each episode as about 40 panel images of around 60 KB each. Most readers open it on phones, often on patchy mobile data while riding a bus. The setup is made up, but the problem is real.
The team opens the browser's network panel on an episode page and sees a staircase. Six panels download, then the next six start, then the next six. Most of each bar is waiting time, not download time.
Two facts explain the whole picture. The image servers speak only HTTP/1.1, which makes requests queue. And on mobile data, some packets get lost on the way. The HTTP version decides what each of those lost packets costs.
Which words do you need before comparing HTTP versions?
You need six: request, connection, round trip, packet, packet loss and head-of-line blocking. Each one gets a plain meaning here, tied to the comics app.
- Request and response: a request is one message from the browser, such as "give me panel 7". The response is the reply, carrying the image and a status code like 200 for success.
- Connection: a long-lived link between the browser and a server. HTTP/1.1 and HTTP/2 use TCP, which delivers bytes reliably and in order. HTTP/3 uses QUIC, which runs on UDP, a simpler transport with no delivery or ordering guarantees.
- Round trip: the time for a message to reach the server and a reply to come back. In this guide, one round trip from a reader's phone to the server takes 80 milliseconds (made up).
- Packet: data travels in small pieces called packets. In this guide's model, each packet carries 1,460 bytes, so a panel of about 60 KB fills about 41 of them.
- Packet loss: sometimes a packet never arrives, and the sender must send it again. The resent data arrives late; in this guide's model, one round trip late.
- Head-of-line blocking: the first item in a line is stuck, so everything behind it waits too. Each HTTP version moved this problem somewhere smaller.
One thing does not change across versions: what HTTP means. A GET is still a GET, a 404 is still a 404, and headers mean the same thing. RFC 9110 defines the parts shared by all versions.
HTTP2 vs HTTP3: what is the real difference?
The real difference between HTTP/2 and HTTP/3 is the transport: TCP versus QUIC over UDP. Almost every other change follows from that switch.
Both versions let one connection carry many requests at once. Over TCP, one lost packet pauses all of them. Over QUIC, it pauses only the requests whose data that packet carried.
QUIC also sets up a new connection one round trip sooner, and a connection can survive a phone switching networks. The next sections show how each version got there, then a table compares all three.
What did HTTP/1.1 fix, and what stayed slow?
HTTP/1.1 fixed the cost of opening a new connection for every request: connections stay open by default. What stayed slow is that each connection still carries only one request and response at a time.
RFC 9112, the current HTTP/1.1 standard, says HTTP/1.1 "defaults to the use of persistent connections". So many requests can reuse one connection. But on that connection, the next response cannot start until the previous one ends.
HTTP/1.1 also defined pipelining, which lets a client send several requests without waiting. The server must still send the responses in the same order the requests arrived. One slow response holds up every response behind it.
MDN's guide notes that no modern browser turns pipelining on by default. Browsers open several connections to the same host instead, commonly up to six. That number explains the staircase in the network panel.
Loading 40 panels over HTTP/1.1
Drag to exploreSix connections, one panel each at a time. Notice the loop: forty panels need seven rounds of request and reply.
Diagram connections:
- Open episode page to Open six TCP connections
- Open six TCP connections to Send one request on each
- Send one request on each to Wait for each full reply
- Wait for each full reply to Panels still missing?
- Panels still missing? to Send one request on each (Yes, next six)
- Panels still missing? to Last panel arrives (No)
- Open episode page: The page lists 40 panel images
- Open six TCP connections: The usual browser limit for one host
- Send one request on each: Six panels in flight at once
- Wait for each full reply: Nothing else may use that connection
- Panels still missing?: Checked after every reply
- Last panel arrives: After seven rounds
Forty panels over six connections means seven rounds. At 80 milliseconds a round trip, that is 560 milliseconds of pure waiting, before counting any download time.
Extra connections are not free either. Each one needs its own TCP handshake and TLS handshake, the setup messages that agree on settings and encryption keys. RFC 9112 asks clients to limit how many connections they open to one server.
HTTP/1.1 also sends headers as plain text on every request. Each panel request repeats the same cookie, user agent and accept lines. A later section measures how much that costs.
What did HTTP/2 fix with streams and header compression?
HTTP/2 fixed queuing inside HTTP by letting many requests share one connection at the same time. It also shrank headers by compressing them.
HTTP/2, defined today in RFC 9113, is binary, not text. Every message is cut into frames, small pieces that each carry a label saying which stream they belong to.
A stream is one request and its response, with its own number. Frames from many streams can be mixed on one connection, and the receiver sorts them back out by stream number. This mixing is called multiplexing.
RFC 9113 states the benefit plainly: a blocked or stalled request or response does not prevent progress on other streams. So all 40 panel requests can leave in the first round trip.
One limit applies. The server announces how many streams it allows at once, in a setting called SETTINGS_MAX_CONCURRENT_STREAMS. RFC 9113 recommends at least 100, well above 40 panels.
Headers get their own fix, called HPACK (RFC 7541). Both sides keep a table of header fields already sent. A repeated cookie can then travel as a short index number instead of the full text.
HTTP/2 also added flow control per stream, so one big download cannot swamp the receiver. Its original priority scheme fared worse. RFC 9113 says that scheme "was not successful" and recommends the simpler one in RFC 9218.
Server push let a server send a file before the browser asked for it. RFC 9113 says push is "difficult to use effectively", because the server must guess correctly what the browser needs. Wrong guesses can make pages slower.
Why can HTTP/2 still stall on a weak mobile network?
HTTP/2 can stall because TCP must hand over bytes in order. If one packet is lost, TCP holds back every byte after it until the resend arrives, even bytes that belong to other streams.
TCP does not know the bytes belong to 40 separate panels. It sees one long line of bytes. RFC 9113 admits it directly: "TCP head-of-line blocking is not addressed by this protocol."
RFC 9114, the HTTP/3 standard, describes the effect on HTTP/2. A lost or reordered packet makes every request in progress stall, whether or not that packet carried its data.
A lassi stall helps here. Picture one counter where the owner reads order slips from a single spike, strictly top to bottom. Every glass ordered must wait its turn on that spike.
- The spike of slips is the TCP connection: one ordered line.
- Each family's order is a stream, and each slip is a packet.
- A slip blown off the counter is a lost packet. The owner stops reading until a new copy arrives, so every family waits.
- HTTP/3 gives each family its own small spike. A missing slip now holds up only that family's glasses.
Where the picture breaks: the owner can see whose slip went missing, while TCP cannot see streams at all. And a real resend is automatic, not a customer rewriting a slip.
The trade looks odd at first. With six HTTP/1.1 connections, a lost packet froze only one of six. With one HTTP/2 connection, it freezes all 40 requests. So HTTP/2 gains most on clean links and gives back part of that gain on lossy mobile data.
What did HTTP/3 and QUIC fix?
HTTP/3 fixed the TCP stall by moving HTTP onto QUIC, a transport where each stream is put back in order on its own. It also shortened connection setup, and lets a connection survive a change of network.
QUIC is defined in RFC 9000 and HTTP/3 in RFC 9114. QUIC packets travel inside UDP. On top, QUIC adds reliability and ordering within each stream, plus congestion control: slowing down when the network is busy.
RFC 9000 is careful about the limit: QUIC does not keep any order between bytes on different streams. That is exactly what lets one stream wait for a resend while the others carry on.
One lost packet, two transports
Drag to exploreTCP pauses every panel for one loss. QUIC pauses only the streams that had data in the lost packet.
Diagram connections:
- A packet is lost to Which transport carries it?
- Which transport carries it? to TCP holds all later bytes (HTTP/2)
- TCP holds all later bytes to All 40 panels pause
- Which transport carries it? to QUIC holds one stream (HTTP/3)
- QUIC holds one stream to Only panel 17 pauses
- A packet is lost: It carried part of panel 17
- Which transport carries it?: Decided by the HTTP version
- TCP holds all later bytes: One ordered byte line for everything
- All 40 panels pause: Until the resend arrives
- QUIC holds one stream: Each stream is reordered on its own
- Only panel 17 pauses: The other 39 keep arriving
Faster setup
QUIC builds TLS 1.3, the encryption behind HTTPS, into its own handshake. RFC 9001 forbids older TLS versions in QUIC. Over TCP, the browser does the TCP handshake first and the TLS handshake after it.
We counted handshake messages in an in-memory test, using Python's ssl module and aioquic 1.7.0. Over TCP with TLS 1.3, the first request can leave after two round trips; with TLS 1.2, after three. A new QUIC connection needs one round trip.
A visitor who connected recently can skip even that with 0-RTT, or early data: the first request rides along with the handshake. TLS 1.3 offers early data over TCP too, but TCP's own handshake still costs one round trip there.
Early data has a real risk. An attacker who copies it can replay it, so the server might run the same request twice. By default, RFC 8470 lets clients send only safe methods such as GET in early data.
Surviving a network switch
A TCP connection is tied to the phone's IP address and port. When a phone moves from Wi-Fi to mobile data, its address changes and plain TCP connections cannot continue.
QUIC identifies a connection by connection IDs instead of addresses. RFC 9000 calls this connection migration, and in QUIC version 1 only the client can move. In our aioquic 1.7.0 test, a client changed address mid-connection, and the server accepted its next request and replied to the new address.
There is a catch for operators. RFC 9000 warns that a load balancer routing by address and port can send a moved connection to the wrong server. Without a fix such as routing by connection ID, the server should switch migration off with a setting called disable_active_migration.
How do HTTP/1.1, HTTP/2 and HTTP/3 compare side by side?
Each version keeps HTTP's meaning and changes how messages travel. The table lines up what each one fixed and what one lost packet still costs.
| HTTP/1.1 | HTTP/2 | HTTP/3 | |
|---|---|---|---|
| First standard | 1997 (RFC 2068); now RFC 9112 | 2015 (RFC 7540); now RFC 9113 | 2022 (RFC 9114), on QUIC (RFC 9000, 2021) |
| Format on the wire | Plain text | Binary frames | Binary frames |
| Transport | TCP | TCP | QUIC over UDP |
| Requests at once per connection | One | Many streams | Many streams |
| What one lost packet stalls | Its own connection only | Every stream on the connection | Only streams with data in that packet |
| Header compression | None | HPACK | QPACK |
| Encryption | Optional | Standard allows none; browsers require TLS | Always TLS 1.3, built in |
| Round trips before first request (new connection) | 2 with TLS 1.3 | 2 with TLS 1.3 | 1 |
| Return visit with early data | 1 (TCP handshake remains) | 1 (TCP handshake remains) | 0 |
| Survives an address change | No | No | Yes, client side, via connection IDs |
HTTP/3 has costs too. Some networks block UDP entirely: RFC 9308 cites measurements of 3 to 5 percent of networks. RFC 9114 tells clients to fall back to a TCP-based version then, so you keep HTTP/2 running beside it.
- HTTP/3 helps most with: mobile users, lossy or changing networks, many parallel requests, and short visits where setup time is a big share.
- It helps least with: clean networks with almost no loss, and long-lived connections inside one data centre, where setup happens rarely.
- It costs you: a second listener on UDP, a TCP fallback you must keep, and replay risk if you turn on early data.
Which HTTP version should your service use?
For anything people load in a browser or app, serve HTTP/2 and HTTP/3 together and keep HTTP/1.1 as a fallback. Each client then picks the best version it supports.
- Readers to the edge servers: HTTP/3 with HTTP/2 fallback. This is where mobile networks, packet loss and network switches happen.
- Edge to origin servers: HTTP/2 or HTTP/1.1 is fine, because both sit in data centres on clean links with long-lived connections.
- Service to service: HTTP/2 is common, partly because gRPC is defined on top of HTTP/2 framing.
- Old clients and simple scripts: keep HTTP/1.1 working, since some tools and devices speak nothing else.
Before and after switching, measure on real traffic. The team compares the time to the first full screen of panels on HTTP/2 and HTTP/3, split by network type. Expect the gap to show on mobile data, not on broadband.
How much does one lost packet cost HTTP/2 in a simple model?
In a simple model of a lossy link, enough to dominate the waiting. A 2 percent loss rate makes about nine in ten HTTP/2 packets wait, against about one in ten for HTTP/3.
The model sends all 40 panels interleaved, the way both versions multiplex them. A lost packet arrives one round trip late. It then counts how many packets arrived on time but had to wait for an earlier lost one.
import random
RTT_MS = 80 # one round trip to the edge
PANELS, PKTS = 40, 41 # 40 panels of about 60 KB, in 1,460-byte packets
GAP_MS = 0.4 # one packet every 0.4 ms (about 29 Mbps)
LOSS = 0.02 # 2 in every 100 packets lost
random.seed(7)
# HTTP/2 and HTTP/3 both interleave the 40 panels: packet k of every panel, then k+1
order = [(panel, k) for k in range(PKTS) for panel in range(PANELS)]
arrive = {}
for i, pkt in enumerate(order):
lost = random.random() < LOSS
arrive[pkt] = i * GAP_MS + (RTT_MS if lost else 0) # resend lands ~1 RTT late
def held_back(line_of):
"""Packets that arrived but had to wait for an earlier lost packet."""
count, wait_ms, ready_at = 0, 0.0, {}
for pkt in order:
line = line_of(pkt)
ready = max(arrive[pkt], ready_at.get(line, 0.0))
ready_at[line] = ready
if ready > arrive[pkt]:
count += 1
wait_ms += ready - arrive[pkt]
return count, round(wait_ms / len(order), 1)
lost = sum(1 for i in range(len(order)) if arrive[order[i]] > i * GAP_MS)
print("packets:", len(order), "lost:", lost)
print("HTTP/2, one TCP byte stream:", held_back(lambda pkt: "tcp"))
print("HTTP/3, one QUIC stream per panel:", held_back(lambda pkt: pkt[0]))The only difference between the two lines is what a packet waits for. For HTTP/2 every packet shares one line, "tcp". For HTTP/3 each panel is its own line, so a loss in panel 17 delays only panel 17.
Run it with python3, no installs needed. It prints 1,640 packets and 43 lost. HTTP/2 holds back 1,491 packets, and HTTP/3 holds back 147.
The second number is the extra wait averaged over all 1,640 packets: 59.2 ms for HTTP/2 against 3.6 ms for HTTP/3. Both are in milliseconds of the model, not a real network.
What just happened? Under HTTP/2, almost every packet sits behind some earlier loss, because a loss anywhere blocks everything after it. Under HTTP/3, only packets of the same panel that come after a loss wait.
Across ten random seeds, HTTP/2 held back 1,461 to 1,607 packets, and HTTP/3 held back 95 to 167. The gap is not luck of one run.
The model is deliberately simple. It ignores download time, and congestion control slowing both versions after a loss. It also assumes every QUIC packet carries data from one panel only.
That last assumption matters. In our aioquic 1.7.0 test, with three 3,000-byte responses, the first packet of each response carried only that response, so dropping one stalled only its own stream. But three 200-byte responses shared one packet, and dropping it stalled all three.
How much do HPACK and QPACK save, and where can QPACK still block?
For repeated requests to the same site, header compression cuts header bytes by about nine times in the test below. QPACK, HTTP/3's version, can still make a stream wait, but only when the receiver allows it and the sender takes the risk.
Measuring HPACK
This script builds the 40 panel requests, with a long session cookie, and encodes them with hpack, the library the Python h2 package uses. Install it with pip install hpack==4.2.0, then run it.
import hpack # pip install hpack==4.2.0
enc = hpack.Encoder()
def request(n):
return [(":method", "GET"), (":scheme", "https"),
(":authority", "comics.example"),
(":path", f"/ep42/panel-{n}.webp"),
("user-agent", "Mozilla/5.0 (Linux; Android 14) Mobile"),
("accept", "image/avif,image/webp,*/*"),
("cookie", "session=" + "a1b2c3d4" * 8)]
# HTTP/1.1 sends the same fields as plain text lines every time
text = "GET /ep42/panel-1.webp HTTP/1.1\r\nHost: comics.example\r\n"
text += "".join(f"{k}: {v}\r\n" for k, v in request(1) if k[0] != ":")
print("HTTP/1.1 text, one request:", len(text) + 2, "bytes")
sizes = [len(enc.encode(request(n))) for n in range(1, 41)]
print("HPACK request 1:", sizes[0], "bytes")
print("HPACK request 2:", sizes[1], "bytes")
print("HPACK all 40:", sum(sizes), "bytes")It prints 226 bytes for one HTTP/1.1 request, 133 bytes for the first HPACK request and 21 bytes for the second. All 40 HPACK requests total 983 bytes, against about 9,000 bytes for the same 40 requests as HTTP/1.1 text.
The first request is still large because the table starts empty. After that, only the changing path costs real bytes. Everything else is a short index into the table both sides now share.
Why HTTP/3 needed QPACK
HPACK assumes every header block arrives in the order it was sent. RFC 9204 explains that using HPACK over QUIC would bring head-of-line blocking back, since QUIC does not order streams.
QPACK sends table updates on a separate one-way stream, called the encoder stream. A request's header block can refer to an entry the receiver has not got yet. That stream is then blocked until the update arrives.
We tested this with pylsqpack 1.0.0. On the second request, the encoder sent 52 bytes of table updates, and the 19-byte header block referred to them. Delivered first, the header block was blocked; once the updates arrived, it decoded.
The receiver limits this risk with a setting called SETTINGS_QPACK_BLOCKED_STREAMS. RFC 9204 says an encoder must never risk more blocked streams than that number. Its default is zero, which means no stream ever waits for the table.
How do you turn on HTTP/3 and check that it works?
Add a QUIC listener on UDP beside your TCP listener, advertise it with an Alt-Svc response header, and open UDP 443 in the firewall. Browsers find HTTP/3 through that header, not by guessing.
Alt-Svc means alternative service. RFC 9114 says an origin can advertise HTTP/3 with the token h3 in it. The ma value is how many seconds clients may remember it; RFC 7838 sets 24 hours when ma is missing.
Choosing between HTTP/1.1 and HTTP/2 happens earlier, inside the TLS handshake. An extension called ALPN, application-layer protocol negotiation (RFC 7301), lets the browser list what it speaks. The server picks one, adding no round trips.
How a browser finds HTTP/3
Drag to exploreThe first visit uses TCP. Notice the decision box: if UDP is blocked, the browser simply stays on HTTP/2.
Diagram connections:
- First visit over HTTP/2 to Reply carries Alt-Svc h3
- Reply carries Alt-Svc h3 to Try QUIC on UDP 443
- Try QUIC on UDP 443 to Did the QUIC handshake work?
- Did the QUIC handshake work? to Use HTTP/3 (Yes)
- Did the QUIC handshake work? to Stay on HTTP/2 (No, UDP blocked)
- First visit over HTTP/2: Browser → Server, over TCP and TLS
- Reply carries Alt-Svc h3: Server → Browser, HTTP/3 on UDP 443
- Try QUIC on UDP 443: Browser → Server, a later connection
- Did the QUIC handshake work?: Browser → Server, UDP may be blocked
- Use HTTP/3: Browser → Server, over QUIC
- Stay on HTTP/2: Browser → Server, over TCP
Here is a server block we ran on nginx 1.28.0. Only the address, port, certificate paths and root differed in our test. HTTP/1.1, HTTP/2 and HTTP/3 requests all returned the same 60,000-byte panel.
# nginx 1.28.0: one server block inside http { }
server {
listen 443 ssl; # TCP: HTTP/1.1 and HTTP/2
listen 443 quic reuseport; # UDP: HTTP/3 over QUIC
http2 on;
http3 on;
ssl_certificate /etc/nginx/certs/site.crt;
ssl_certificate_key /etc/nginx/certs/site.key;
ssl_protocols TLSv1.2 TLSv1.3;
add_header Alt-Svc 'h3=":443"; ma=86400' always;
root /var/www/comics;
}Both listen lines use port 443, one on TCP and one on UDP, so the firewall must allow both. The always flag adds Alt-Svc to every reply, including errors; without it nginx adds the header only to success and redirect codes. nginx's own docs still label its HTTP/3 module experimental, as of October 2026.
Now check it from a terminal. Replace YOUR_SITE with your host name. The expected output matches our test server, except that its port was 8443.
# 1. Is HTTP/3 advertised? (any curl)
curl -sI https://YOUR_SITE/ | grep -i alt-svc
# expect: alt-svc: h3=":443"; ma=86400
# 2. Which version does a TCP connection get?
curl -s -o /dev/null -w '%{http_version}\n' https://YOUR_SITE/
# expect: 2 (with --http1.1 added, expect: 1.1)Testing HTTP/3 itself needs a client built with it, such as a curl build that supports the --http3 option. A curl without it, such as the curl 8.5.0 we tested, prints "option --http3: the installed libcurl version doesn't support this". That says nothing about your server.
Leave 0-RTT off unless your app can handle replays. nginx's ssl_early_data directive is off by default. For HTTP/3, its docs say 0-RTT cannot be enabled with OpenSSL before nginx 1.29.1, and needs OpenSSL 3.5.1 or later (as of October 2026).
How do you explain HTTP/1.1 vs HTTP/2 vs HTTP/3 in a system design interview?
Tell it as one problem moving: each version moved head-of-line blocking to a smaller place. Then tie your choice to the users in the question.
Say what stays the same
All three versions share HTTP's meaning: methods, status codes and headers. Only how messages travel on the wire changes.
HTTP/1.1: one at a time
Connections are reused, but each carries one request at a time. Browsers open up to about six connections per host to work around it.
HTTP/2: many streams, one TCP connection
Binary frames and streams let many requests share one connection, and HPACK shrinks repeated headers. One lost TCP packet still stalls every stream.
HTTP/3: QUIC over UDP
A loss stalls only the streams in the lost packet. TLS 1.3 is built into a one-round-trip setup, and connection IDs let clients change networks.
Name the costs
UDP is blocked on some networks, so HTTP/2 fallback stays. Early data can be replayed. Simple address-based load balancers break migration.
Fit it to the design
Use HTTP/3 at the edge for mobile users, HTTP/2 or HTTP/1.1 inside the data centre, and measure first-screen time by network type.
Questions people ask
- Is HTTP/3 always faster than HTTP/2?
- No. Its gains come from per-stream loss recovery and one round trip less setup, so they matter most with packet loss, long round trips and fresh connections. With no loss, the model in this guide shows no held-back packets for either version. Measure on your own users before you promise a speed-up.
- Does HTTP/3 replace TCP?
- For HTTP/3 traffic, yes. HTTP/3 runs on QUIC, which runs on UDP, so no TCP connection is used. TCP is not going away, though. HTTP/1.1, HTTP/2 and many other protocols still depend on it, and HTTP/3 clients fall back to TCP when UDP is blocked.
- Do I need to change my API code to support HTTP/2 or HTTP/3?
- Usually not. The meaning of requests and responses is the same in all three versions. The web server, load balancer or HTTP library handles the wire format. You mostly change server settings, certificates and firewall rules, and you keep an eye on early data if you turn it on.
- Why does HTTP/3 use UDP instead of a new transport protocol?
- Because UDP already passes through most networks. RFC 9308 says building on UDP lets QUIC run in ordinary user programs and cross existing middleboxes, the routers and firewalls in between, without updating them. QUIC adds reliability, per-stream ordering and encryption on top.
- What is head-of-line blocking in HTTP/2?
- It is the stall when one lost TCP packet holds back every stream on the connection. HTTP/2 removed queuing between requests at the HTTP layer. But TCP still hands over bytes strictly in order, so all streams wait for the resend, even those whose data arrived.
- Does HTTP/2 require HTTPS?
- The standard does not. RFC 9113 allows HTTP/2 over plain TCP when the client knows in advance that the server supports it. Browsers, though, use HTTP/2 only over TLS, so public websites run it over HTTPS. HTTP/3 is always encrypted, because QUIC has TLS 1.3 built in.
- Is HTTP/1.1 still used today?
- Yes. Servers keep it as a fallback for clients and tools that speak nothing newer, and it is still an option between a proxy and the servers behind it. Its plain-text format also makes it easy to read while debugging with simple tools.
Sources and further reading
- RFC 9110: HTTP semantics, abstract (IETF, June 2022)
- RFC 9112: HTTP/1.1, sections 9.3 and 9.4 (IETF, June 2022)
- RFC 9113: HTTP/2, sections 1, 2, 3.3, 5.3, 6.5.2 and 8.4 (IETF, June 2022)
- RFC 7541: HPACK header compression for HTTP/2 (IETF, May 2015)
- RFC 9218: Extensible prioritization scheme for HTTP (IETF, June 2022)
- RFC 9114: HTTP/3, sections 1 and 3.1 (IETF, June 2022)
- RFC 9000: QUIC transport, sections 1, 2, 5.1 and 5.2.3 (IETF, May 2021)
- RFC 9001: Using TLS to secure QUIC, section 4.2 (IETF, May 2021)
- RFC 9204: QPACK field compression for HTTP/3, section 2.1.2 (IETF, June 2022)
- RFC 9308: Applicability of the QUIC transport protocol, section 2 (IETF, September 2022)
- RFC 8470: Using early data in HTTP (IETF, September 2018)
- RFC 7838: HTTP alternative services, section 3.1 (IETF, April 2016)
- RFC 7301: TLS application-layer protocol negotiation, section 1 (IETF, July 2014)
- MDN Web Docs: Connection management in HTTP/1.x
- nginx docs: Module ngx_http_v3_module
- nginx docs: Module ngx_http_headers_module, add_header directive
- nginx docs: Module ngx_http_ssl_module, ssl_early_data directive