Зачем три версии
HTTP - прикладной протокол поверх [[tcp-handshake|TCP]] (или [[udp-basics|UDP]] в HTTP/3). Обновлялся дважды чтобы решить накопленные проблемы производительности. Версии взаимно понимаемы через [[tls-handshake|TLS]] ALPN: клиент и сервер договариваются при handshake'е.
HTTP/1.1 (1997)
Текстовый запрос-ответ. Каждый запрос - один request/response через CRLF-разделители:
GET /api/users HTTP/1.1
Host: example.com
User-Agent: curl/8.0
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 42
{"users":[{"id":1,"name":"alice"}]}Главные фичи:
- Host header - один IP, много vhost'ов
- Keep-alive - переиспользовать TCP-соединение для нескольких запросов (по дефолту on)
- Chunked transfer encoding - стриминг ответа без знания Content-Length заранее
- Pipelining - слать запросы не дождавшись ответа (на практике выключают - HoL blocking, см. ниже)
Главная боль HTTP/1.1:
- Один запрос за раз в одном соединении (Head-of-Line blocking на L7)
- Браузеры открывают 6+ TCP-соединений к одному хосту чтобы качать параллельно
- Заголовки повторяются в каждом запросе (cookies, user-agent…)
HTTP/2 (2015) - бинарный мультиплекс
Тот же GET /api/users остался, но формат бинарный:
- Один TCP-соединение содержит много streams (логических каналов)
- Streams мультиплексируются - кадры из разных запросов чередуются
- HPACK - сжатие заголовков; повторяющиеся куки/токены передаются индексом, не строкой
- Server Push - сервер может слать ресурсы до запроса клиента (на практике задеприкейтили - редко полезно)
- Stream priority - сказать «этот CSS важнее, чем картинка»
В сухом остатке: один TCP-сокет на host вместо 6, заголовки сжаты, параллельные запросы не блокируются друг другом на L7.
Но! Если потерялся пакет в TCP - все streams в этом соединении блокируются до ретрансмита. Это TCP head-of-line blocking, который HTTP/2 не решает, только выявляет.
HTTP/3 (2022) - QUIC заменяет TCP
Та же бинарная семантика, что HTTP/2, но транспорт - QUIC поверх UDP:
- Каждый stream живёт независимо - потеря пакета в одном стриме не блокирует другие (нет TCP-HoL)
- Handshake = 1 RTT (или 0 RTT для повторных подключений) - в QUIC TLS встроен
- Connection migration - смена IP (Wi-Fi → 4G) не разрывает соединение, QUIC привязывается к Connection ID, не к 5-tuple
- Шифрование обязательно
Минусы HTTP/3:
- UDP часто рейтлимитится firewall'ами и LB
- Сложнее наблюдать (нет нативного netstat для QUIC до недавно)
- Требует ALPN
h3через TLS 1.3
Сравнение
| Свойство | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|
| Транспорт | TCP | TCP | QUIC/UDP |
| Формат | текст | бинарь | бинарь |
| Multiplexing | нет | да | да |
| Head-of-line blocking | L4 + L7 | L4 (TCP) | нет |
| Сжатие заголовков | нет | HPACK | QPACK |
| TLS | опц. | опц. | обязателен |
| Server Push | нет | да* | да* |
*устарел, не используется
Где видно в tcpdump
# HTTP/1.1, текст в plain
tcpdump -i any -nn -A 'tcp port 80'
# HTTP/2, бинарь, видно ALPN-маркер 'h2' в ClientHello
tcpdump -i any -nn 'tcp port 443' -X
# HTTP/3, UDP/443
tcpdump -i any -nn 'udp port 443'
Какой выбрать в 2026
- CDN / edge - HTTP/3 для пользователей через Wi-Fi, fallback HTTP/2
- internal API - HTTP/2 (один долгоживущий соединение, gRPC по нему)
- legacy / простота - HTTP/1.1, парсинг из любого языка двумя строками
Когда что-то пошло не так
- HTTP/2 RST_STREAM rapid-reset - DDoS-вектор 2023; патчи в nginx/envoy
- QUIC заблокирован firewall'ом - падение в HTTP/2 (Alt-Svc)
- HPACK bomb - вредоносный Huffman через сжатие заголовков; обновляться
- Header limit - HTTP/2 по умолчанию 16KB заголовков;
large requestне пройдёт без увеличения