Этап 6 — HTTP/2 и HTTP/3
HTTP/1.1 — двадцатилетний динозавр. HTTP/2 решил его главные проблемы. HTTP/3 пошёл дальше — взял другой транспорт. Понимать разницу — критично, потому что разработчики до сих пор оптимизируют сайты под HTTP/1.1 и теряют в 2-3 раза скорость.
«Ретро после миграции на HTTP/2: в офисном Wi-Fi сайт стал быстрее в два раза, а у мобильных пользователей в метро — МЕДЛЕННЕЕ, чем был на HTTP/1.1. Метрики не врут». Прежде чем читать: как один и тот же протокол может ускорять в хорошей сети и замедлять в плохой? Подсказка: подумай, что общего у всех запросов страницы в HTTP/2 — и что происходит, когда в сети теряются пакеты. Разгадка — в разделе 6, и она же объясняет, зачем вообще существует HTTP/3.
- Назвать четыре родовые болезни HTTP/1.1 и показать, какими механизмами HTTP/2 закрыл каждую
- Объяснить frames и streams — как десятки запросов едут вперемешку по одному соединению
- Показать границу HTTP/2: почему TCP head-of-line blocking им не решается в принципе
- Рассказать QUIC: зачем поверх UDP, где живёт TLS, что даёт connection migration
- Объяснить, как клиент вообще узнаёт про HTTP/3 (Alt-Svc и DNS-запись HTTPS)
- Проверить протокол реального сайта:
curl --http2/--http3, колонка Protocol в DevTools - Вычистить вредные «оптимизации» эпохи HTTP/1.1: sharding, sprites, конкатенацию
Всё нужное уже в руках: из этапа 4 — что потерянный TCP-сегмент останавливает весь поток до ретрансмита (это станет главным героем урока) и что такое RTT и slow start; из этапа 5 — ALPN, которым клиент и сервер договорились о «h2», и 1-RTT handshake, который QUIC умудрится склеить с транспортным. Из этапа 1 — лимит «6 соединений на хост» в HTTP/1.1 и connection coalescing.
1. Почему HTTP/1.1 — это плохо в 2026
HTTP/1.1 (RFC 2616, 1999) спроектирован для одного запроса за раз:
- Один запрос — один ответ. Нельзя отправить второй, пока не пришёл первый.
- Текстовый протокол. Парсинг затратнее бинарного.
- Заголовки повторяются. 1000 ресурсов = 1000 раз отправляешь Cookie/User-Agent.
- Head-of-line blocking. Медленный запрос блокирует все следующие в этом соединении.
Браузеры обходят это, открывая 6 параллельных соединений на хост. Каждое — отдельный TCP+TLS handshake. Это медленно и дорого.
2. HTTP/2 — бинарный мультиплекс
RFC 7540 (2015). Главные идеи:
🔢 Бинарный формат
Не текст, а структурированные frames. Парсинг моментальный, ошибок меньше.
🔀 Multiplexing
Сотни запросов одновременно в одном TCP-соединении. Никаких очередей.
🗜️ HPACK
Сжатие заголовков. Повторяющиеся headers занимают 1-2 байта.
⚖️ Priorities
Клиент может сказать «CSS важнее, чем картинка». Сервер шлёт критичное первым.
Frames и streams
HTTP/2-соединение — это поток frames. Каждый frame принадлежит какому-то stream. Один stream = один запрос + ответ.
┌─ HTTP/2 connection (один TCP+TLS) ──────────────┐
│ ┌─ Stream 1: GET /page.html ──────┐ │
│ │ HEADERS frame │ │
│ │ DATA frame │ │
│ └─────────────────────────────────┘ │
│ ┌─ Stream 3: GET /style.css ─────┐ │
│ │ HEADERS frame │ │
│ │ DATA frame │ │
│ │ DATA frame │ │
│ └────────────────────────────────┘ │
│ ┌─ Stream 5: GET /app.js ────────┐ │
│ │ ... │ │
│ └────────────────────────────────┘ │
└──────────────────────────────────────────────────┘
↑ Frames из разных streams перемежаются в одном TCP-потоке
ID streams нечётные (1, 3, 5...) для клиент-инициированных, чётные — для server-initiated (push).
Типы frames
| Frame | Что несёт |
|---|---|
| HEADERS | Заголовки запроса/ответа (через HPACK) |
| DATA | Тело |
| SETTINGS | Параметры соединения (max streams, window size) |
| WINDOW_UPDATE | Flow control: «я готов принять ещё столько» |
| PRIORITY | Приоритет stream (устарел в HTTP/2 → удалён в /3) |
| RST_STREAM | Отменить stream |
| PING | Keep-alive / latency check |
| GOAWAY | «Закрываю соединение» |
| PUSH_PROMISE | Server push (устарел) |
3. HPACK — сжатие заголовков
Заголовки в HTTP/1.1 — это ~700 байт на запрос (Cookie, User-Agent, Accept, ...). Если ты грузишь 100 ресурсов — это 70 КБ только заголовков, и они повторяются.
HPACK решает это двумя способами:
- Статическая таблица. Часто используемые имена заголовков заранее проиндексированы.
:method GET= индекс 2. Один байт. - Динамическая таблица. Когда клиент шлёт
cookie: session=abc123, он добавляет это в общую таблицу. Сервер тоже добавляет. В следующем запросе клиент шлёт только индекс — например,62. - Huffman. Значения шифруются Huffman-кодом.
В результате — заголовки сжимаются в 5-10 раз. На запрос вместо 700 байт — 50-100.
4. Flow control в HTTP/2
В TCP flow control работает на уровне соединения. В HTTP/2 — добавляется ещё и на уровне stream. Зачем?
Допустим, ты качаешь огромный файл (stream 1) и одновременно делаешь маленький API-запрос (stream 3). Если канал «забит» большим файлом, мелкий не пройдёт — head-of-line blocking. Поэтому HTTP/2 даёт окно отдельно каждому stream.
WINDOW_UPDATE frame: «по stream X я готов принять ещё N байт».
5. Server Push — было, ушло
HTTP/2 ввёл server push: «сервер заранее знает, что клиент попросит CSS, и шлёт его без запроса». Идея красивая, но на практике не сработала:
- Сервер не знает, что у клиента уже в кэше → шлёт лишнее
- Сложно настроить правильно
- Cache problems — пушнутые ресурсы плохо кэшируются
Chrome выключил push в 2022. HTTP/3 убрал его. Используй 103 Early Hints вместо него — это решает ту же проблему лучше.
6. Главный недостаток HTTP/2 — TCP HoL blocking
HTTP/2 устранил HoL blocking на уровне HTTP — один stream не блокирует другие. Но он не убрал HoL blocking на уровне TCP:
Все streams идут в одном TCP. Если в TCP потеряется один пакет — TCP останавливается, пока пакет не пересылается. Несмотря на то, что данные для других streams уже могли прийти.
В плохих сетях (мобильный 4G, Wi-Fi на стадионе) это убивает HTTP/2 — он становится медленнее, чем HTTP/1.1 с шестью соединениями.
Одна труба против шести — и почему в плохую погоду выигрывают шесть
HTTP/1.1 — это шесть узких труб: по каждой ползёт один запрос за другим, зато затор в одной не мешает остальным пяти. HTTP/2 — одна широкая труба, в которой все посылки едут вперемешку, помеченные номерами потоков: в хорошей сети это сильно быстрее — не надо строить шесть труб (шесть handshake) и каждая посылка не ждёт очереди. Но труба-то одна, и лежит она на TCP: если один сегмент утонул, заслонка закрывается для всех, пока его не перешлют — даже для посылок, которые уже доплыли. Это и есть TCP head-of-line blocking. QUIC (следующий раздел) строит внутри одной трубы независимые дорожки: утонувшая посылка задерживает только свою дорожку.
7. QUIC — новый транспорт
Google разработал QUIC в 2012, IETF стандартизировал в 2021 (RFC 9000). Это новый транспортный протокол поверх UDP.
Идея: «а если перенесём всё, что делает TCP, на уровень приложения — но умнее?»
Что выиграно:
- Нет TCP HoL blocking. Потеря пакета затрагивает только тот stream, в котором она произошла.
- 0-RTT handshake. TLS встроен в QUIC, оптимизированный.
- Connection migration. Сменил Wi-Fi на 4G → IP поменялся, но соединение продолжает работать. У QUIC connection ID, а не 4-tuple.
- Лучший контроль перегрузки. Реализован в приложении, обновляется без апдейта ядра ОС.
- Шифрование всего. Даже заголовки QUIC зашифрованы — middleboxes не могут вмешиваться.
8. QUIC внутри
Connection ID
В TCP соединение идентифицируется 4-tuple (src IP, src port, dst IP, dst port). Сменил сеть — 4-tuple изменился — соединение разорвалось.
В QUIC у соединения собственный connection ID — случайное число, согласованное при handshake. Не зависит от IP. Поэтому при смене сети ничего не разрывается — клиент шлёт пакеты с новым source IP, сервер сопоставляет по connection ID.
Streams в QUIC
QUIC реализует streams сам, не на уровне HTTP. У каждого stream свой sequence number. Потерянный пакет блокирует только свой stream.
QPACK вместо HPACK
HPACK имел проблему: если ты обновляешь динамическую таблицу, и frame с обновлением застрял — следующие запросы блокированы (опять HoL!). QPACK переделан так, чтобы streams могли работать независимо.
0-RTT в QUIC
Как и TLS 1.3 — клиент может вложить данные сразу. Те же ограничения (только идемпотентные).
9. Alt-Svc — как сайт говорит «у меня есть HTTP/3»
Браузер сначала открывает сайт по HTTP/2. Сервер шлёт заголовок:
Alt-Svc: h3=":443"; ma=86400
Это значит: «попробуй HTTP/3 на этом же хосте, порт 443, кэшируй на 24 часа». Браузер в следующих визитах сразу подключается через QUIC.
Альтернатива — DNS-запись HTTPS:
example.com. HTTPS 1 . alpn="h3,h2"
Браузер узнаёт о HTTP/3 ещё до первого подключения.
10. HTTP/3 frames — другие, но похожие
В HTTP/3 — свой набор frames, упрощённый по сравнению с HTTP/2 (часть работы делает QUIC):
- HEADERS
- DATA
- SETTINGS
- GOAWAY
- CANCEL_PUSH (push был, но удалён)
Нет: WINDOW_UPDATE (QUIC сам), PING (QUIC сам), PRIORITY (отдельная extension).
11. Сравнение в цифрах
| HTTP/1.1 | HTTP/2 | HTTP/3 | |
|---|---|---|---|
| Транспорт | TCP | TCP | QUIC/UDP |
| Соединение | До 6 параллельных | Одно | Одно |
| Format | Текст | Бинарный | Бинарный |
| Multiplexing | Нет | Да (stream-level) | Да + без TCP HoL |
| Header compression | Нет | HPACK | QPACK |
| Handshake RTT (новый) | 2-3 (TCP+TLS) | 2-3 | 1 (QUIC объединяет TCP+TLS) |
| 0-RTT | — | Через TLS 1.3 | Встроено |
| Connection migration | Нет | Нет | Да |
| Шифрование | Опционально | Опционально (но всегда HTTPS) | Встроено и обязательно |
12. Adoption в 2026
По данным Cloudflare / W3Techs (округлённо):
- HTTP/2: ~80% сайтов используют
- HTTP/3: ~30% сайтов поддерживают, особенно у Cloudflare, Google, Facebook, YouTube
- HTTP/1.1: только legacy и небольшие сайты без CDN
Браузеры (Chrome, Firefox, Safari) все поддерживают HTTP/3 и автоматически переключаются.
13. Подводные камни
UDP блокируется в корпоративных сетях
Старые firewalls режут UDP на порту 443. Браузер пробует HTTP/3, fail-back на HTTP/2. Это медленно при первом подключении. Решение — Alt-Svc Happy Eyeballs.
UDP rate limit
Некоторые провайдеры искусственно режут UDP. QUIC работает медленно.
QUIC замечен «карательно»
В странах с глубокой инспекцией трафика — QUIC может блокироваться, потому что middleboxes не видят, что внутри. С ECH это станет ещё «слепее».
HTTP/3 не везде в облаке
AWS ALB — поддерживает HTTP/2, но не HTTP/3 (на середину 2024). Cloudflare и Fastly — оба. AWS CloudFront — да.
14. Мини-лаба: попробуй прямо сейчас
curl с поддержкой HTTP/3 есть в свежих дистрибутивах (проверь curl --version: флаги HTTP2/HTTP3); если --http3 нет — первые команды всё равно работают:
# Какой протокол согласовался
curl -v --http2 https://example.com 2>&1 | grep -i 'http/'
curl -v --http3 https://www.cloudflare.com 2>&1 | grep -i 'http/'
# Только заголовки + версия
curl -sI -o /dev/null -w '%{http_version}\n' https://example.com
# Alt-Svc — посмотреть, есть ли HTTP/3
curl -sI https://www.cloudflare.com | grep -i alt-svc
# Проверить, реально ли подключаешься по HTTP/3
# Chrome DevTools → Network → колонка Protocol должна показать h3
# Захват QUIC-трафика
sudo tcpdump -i any -nn 'udp port 443'
# Декодировать QUIC в Wireshark — нужны ключи (SSLKEYLOGFILE)
# В Chrome: export SSLKEYLOGFILE=/tmp/keys.log; google-chrome
Первый curl напечатает using HTTP/2 и заголовки в нижнем регистре — примета
бинарного протокола. В ответе Cloudflare найди alt-svc: h3=":443" — это приглашение
перейти на HTTP/3. В DevTools → Network (включи колонку Protocol правой кнопкой по шапке
таблицы) у крупных сайтов увидишь h3 — а при первом заходе иногда h2,
который на следующих запросах сменится на h3: браузер прочитал Alt-Svc и мигрировал. В
tcpdump-захвате QUIC — только нечитаемый UDP на 443: даже тип пакета зашифрован, вот почему
сети не умеют вмешиваться в QUIC.
15. Что с этим делает DevOps
- Включить HTTP/2. Это бесплатное ускорение. На Nginx — одна строка.
- Включить HTTP/3. Cloudflare/Fastly — checkbox. CloudFront — поддерживается. Self-hosted Nginx — есть QUIC, но требует свежей сборки.
- Alt-Svc или DNS HTTPS-record. Чтобы браузеры узнали о HTTP/3.
- Не оптимизировать под HTTP/1.1. Domain sharding, CSS sprites, file concatenation — всё это вредно в HTTP/2+. Делает кэширование хуже.
- Мониторить protocol distribution. CloudFront / Cloudflare показывают, сколько трафика по каждой версии. Если HTTP/1.1 много — копай.
- Frame sizes / window sizes. В тюнинге Nginx можно подкрутить, для больших файлов это даёт прирост.
- Connection draining. При деплое — graceful shutdown. HTTP/2-соединения держатся минутами, нужно дать им закрыться правильно.
16. Задачи самопроверки — реши, потом раскрой ответ
Задача 1. Та самая ситуация из начала урока: после перехода на HTTP/2 мобильные пользователи в метро стали грузиться медленнее, офисные — быстрее. Объясни механизм и предложи решение без отката на HTTP/1.1.
В метро высокие потери пакетов. HTTP/2 везёт все ресурсы в одном TCP-соединении, и каждый потерянный сегмент останавливает ВСЕ потоки до ретрансмита (TCP HoL blocking) — тогда как шесть соединений HTTP/1.1 деградировали независимо. Решение — HTTP/3: QUIC ведёт потоки независимо, потеря тормозит только свой stream. Включить h3 на CDN + Alt-Svc/HTTPS-запись, мобильные браузеры переползут сами.
Задача 2. Во фронтенд-репозитории ты находишь сборку, которая склеивает все JS в один бандл на 4 МБ, спрайтит иконки и раскидывает статику по четырём доменам-шардам. Сайт давно на HTTP/2. Что из этого теперь вредно и почему?
Всё три — оптимизации под HTTP/1.1. Шардинг мешает мультиплексированию и coalescing: браузер строит лишние соединения и хуже приоритизирует. Мегабандл и спрайты ломают кэширование: правка одной строки инвалидирует 4 МБ у всех клиентов, тогда как HTTP/2 спокойно тянет сотни мелких файлов по одному соединению — и кэш обновляется точечно. Убирать шардинг, дробить бандлы, спрайты — только там, где иконки реально меняются вместе.
Задача 3. Пользователь едет в поезде: телефон прыгает между Wi-Fi вагона и LTE. На HTTP/2 каждое переключение — обрыв и повторное соединение, на HTTP/3 видео даже не икает. За счёт чего?
Connection migration. TCP-соединение определяется четвёркой (IP/порты) — сменился IP, умерло соединение, новый handshake. QUIC идентифицирует соединение по Connection ID, а не по адресам: телефон сменил сеть, первый же пакет с новым IP, но старым CID продолжает ту же сессию — без рукопожатий и потери состояния.
Задача 4. Безопасники просят «развернуть инспекцию HTTP/3-трафика на периметре, как мы делали для HTTP/1.1». Что им придётся узнать про QUIC?
QUIC шифрует практически всё, включая транспортные заголовки — извне виден только UDP на 443 с Connection ID. Прозрачная инспекция «по дороге» невозможна by design. Варианты: терминировать QUIC на своём прокси/CDN (легальный MITM с своим сертификатом) и инспектировать расшифрованное, либо блокировать UDP/443 и заставлять клиентов падать на h2 (так делают многие корпоративные сети — браузеры откатываются автоматически).
Задача 5. На собеседовании: «Раз HTTP/2 сжимает заголовки HPACK'ом, почему HTTP/3 понадобился другой алгоритм QPACK?»
HPACK строит динамическую таблицу, которую обе стороны обновляют строго последовательно — это работало, пока TCP гарантировал порядок. В QUIC потоки независимы и приходят в любом порядке: последовательной доставки для таблицы больше нет. QPACK разводит обновления таблицы в отдельные однонаправленные потоки и допускает ссылки с учётом подтверждений — сжатие чуть слабее, зато без блокировок между потоками.
17. Словарик урока
Запрос долетел до edge по h2/h3 — и тут его встречает вышибала. Прежде чем пустить трафик к origin, edge должен решить: это человек или бот? Легитимный запрос или SQL-инъекция? Один из миллиона нормальных или один из миллиона DDoS? Следующий этап — WAF, rate limiting и вся безопасность на границе.
Чек-лист «понимаю это»
- ☐ Объясню, чем плох HTTP/1.1 в 2026
- ☐ Понимаю frames и streams в HTTP/2
- ☐ Знаю, что такое HPACK и зачем нужно
- ☐ Расскажу про TCP HoL blocking и почему HTTP/2 не решает
- ☐ Объясню QUIC: поверх UDP, встроенный TLS, connection ID
- ☐ Помню про connection migration в QUIC
- ☐ Знаю про QPACK и почему он не как HPACK
- ☐ Помню, что Server Push устарел
- ☐ Понимаю роль Alt-Svc и HTTPS DNS record
- ☐ Использовал
curl --http3и проверил, что работает
103 Early Hints. Современные оптимизации фронта (никаких sprites/concat) рассчитаны на HTTP/2+. HTTP/3 уже у Cloudflare/Google/Meta, скоро везде.