MidТеория3 min

HTTP/1.1 vs HTTP/2 vs HTTP/3

Эволюция HTTP: мультиплексирование, сжатие заголовков, server push, QUIC и практический выбор версии протокола

Эволюция HTTP

HTTP (HyperText Transfer Protocol) -- основной протокол веба. За 30 лет он прошёл путь от простого текстового протокола до высокопроизводительного бинарного транспорта.

1991    1997     1999        2015         2022
 │       │        │           │            │
HTTP/0.9 HTTP/1.0 HTTP/1.1   HTTP/2       HTTP/3
Только   Заголовки Keep-alive Мультиплекс  QUIC
GET      статусы  Chunked    Бинарный     0-RTT
         коды     Pipelining HPACK        QPACK
                  Host       Server Push  UDP

Зачем знать различия для System Design

  • Выбор версии HTTP влияет на производительность API
  • Понимание мультиплексирования важно при проектировании микросервисов
  • HTTP/3 (QUIC) меняет поведение в нестабильных сетях (мобильные клиенты)
  • На собеседовании: «Как бы вы оптимизировали взаимодействие между сервисами?»

HTTP/1.1

Ключевые особенности

Клиент                              Сервер
  │                                   │
  │── GET /index.html ──────────────►│
  │◄── 200 OK + HTML ────────────────│
  │                                   │
  │── GET /style.css ───────────────►│  ← Последовательно!
  │◄── 200 OK + CSS ─────────────────│
  │                                   │
  │── GET /script.js ───────────────►│  ← Ждёт предыдущий ответ
  │◄── 200 OK + JS ──────────────────│
Свойство Описание
Keep-Alive Повторное использование TCP-соединения
Pipelining Отправка запросов без ожидания ответа (на практике отключено)
Chunked Transfer Потоковая передача (без Content-Length)
Host заголовок Виртуальные хосты на одном IP
Текстовый формат Человекочитаемые заголовки

Проблемы HTTP/1.1

Head-of-Line Blocking:

Соединение 1: GET /large-image.jpg ────────────────────► (2 сек)
              GET /small-api.json  ─── ЗАБЛОКИРОВАН ──►  (ждёт image)

Обходное решение: 6 параллельных TCP-соединений к одному хосту
  Соединение 1: GET /image1.jpg ──────────────────────►
  Соединение 2: GET /image2.jpg ──────────────────────►
  Соединение 3: GET /api/data   ──────────────────────►
  Соединение 4: GET /style.css  ──────────────────────►
  Соединение 5: GET /script.js  ──────────────────────►
  Соединение 6: GET /font.woff  ──────────────────────►

Избыточность заголовков:

Каждый запрос несёт полные заголовки (~800 байт):

GET /api/v1/users HTTP/1.1
Host: api.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64)...
Accept: application/json
Accept-Language: ru-RU,ru;q=0.9,en-US;q=0.8
Accept-Encoding: gzip, deflate, br
Cookie: session=abc123; tracking=xyz789; preferences=...
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
Connection: keep-alive

↑ Эти заголовки повторяются в КАЖДОМ запросе!

HTTP/2

Бинарное фреймирование

HTTP/2 использует бинарный формат вместо текстового. Каждое сообщение разбивается на фреймы.

HTTP/1.1 (текст):           HTTP/2 (бинарный):
                             ┌─────────────┐
GET /index.html HTTP/1.1     │  HEADERS     │ Stream 1
Host: example.com      →     │  Frame       │
Accept: text/html            └─────────────┘
                             ┌─────────────┐
                             │  DATA        │ Stream 1
                             │  Frame       │
                             └─────────────┘

Мультиплексирование

Главное преимущество HTTP/2 -- множество параллельных запросов в одном TCP-соединении.

HTTP/1.1: 6 TCP-соединений              HTTP/2: 1 TCP-соединение
┌──TCP──┐ ┌──TCP──┐ ┌──TCP──┐          ┌────────────TCP─────────────┐
│ Req 1 │ │ Req 2 │ │ Req 3 │          │ Stream 1: HEADERS + DATA   │
│ Res 1 │ │ Res 2 │ │ Res 3 │          │ Stream 3: HEADERS + DATA   │
└───────┘ └───────┘ └───────┘          │ Stream 5: HEADERS + DATA   │
┌──TCP──┐ ┌──TCP──┐ ┌──TCP──┐          │ Stream 7: HEADERS + DATA   │
│ Req 4 │ │ Req 5 │ │ Req 6 │          │ Stream 9: HEADERS + DATA   │
│ Res 4 │ │ Res 5 │ │ Res 6 │          │ Stream 11: HEADERS + DATA  │
└───────┘ └───────┘ └───────┘          └────────────────────────────┘
6 TCP handshakes + 6 TLS               1 TCP handshake + 1 TLS

Фреймы могут чередоваться (interleaving):

Одно TCP-соединение:

┌──────┬──────┬──────┬──────┬──────┬──────┬──────┐
│ H(1) │ H(3) │ D(1) │ D(3) │ D(1) │ H(5) │ D(5) │
└──────┴──────┴──────┴──────┴──────┴──────┴──────┘
  │       │      │      │      │      │      │
  │       │      │      │      │      │      └── Data Stream 5
  │       │      │      │      │      └── Headers Stream 5
  │       │      │      │      └── Data Stream 1 (continued)
  │       │      │      └── Data Stream 3
  │       │      └── Data Stream 1
  │       └── Headers Stream 3
  └── Headers Stream 1

HPACK: Сжатие заголовков

HTTP/2 использует HPACK для сжатия заголовков, устраняя избыточность.

Первый запрос: все заголовки передаются полностью
┌─────────────────────────────────────────────┐
│ :method: GET                                │
│ :path: /api/v1/users                        │
│ :authority: api.example.com                 │
│ authorization: Bearer eyJhbGci...           │
│ accept: application/json                    │
└─────────────────────────────────────────────┘
  → Заголовки добавляются в динамическую таблицу

Второй запрос: только различия
┌─────────────────────────────────────────────┐
│ :path: /api/v1/orders    ← Изменилось       │
│ (остальные = ссылки на индексы в таблице)    │
└─────────────────────────────────────────────┘
  → Экономия ~90% размера заголовков

Статическая таблица HPACK содержит 61 самый частый заголовок:

Индекс Заголовок Значение
1 :authority
2 :method GET
3 :method POST
4 :path /
5 :path /index.html
8 :status 200
... ... ...

Server Push

Сервер может отправить ресурсы ДО того, как клиент их запросит.

Клиент                              Сервер
  │                                   │
  │── GET /index.html ──────────────►│
  │                                   │  Сервер знает, что для index.html
  │◄── PUSH_PROMISE /style.css ──────│  нужны style.css и script.js
  │◄── PUSH_PROMISE /script.js ──────│
  │◄── 200 OK /index.html ───────────│
  │◄── 200 OK /style.css (pushed) ───│  ← Клиент не запрашивал!
  │◄── 200 OK /script.js (pushed) ───│  ← Клиент не запрашивал!

На практике: Server Push в HTTP/2 оказался сложным в использовании и был удалён из Chrome в 2022 году. Лучшая альтернатива -- 103 Early Hints.

Stream Priority

HTTP/2 позволяет задавать приоритеты потокам:

Stream 1 (HTML):    вес 256  ← Максимальный приоритет
Stream 3 (CSS):     вес 220  ← Высокий (блокирует рендер)
Stream 5 (JS):      вес 183  ← Средний
Stream 7 (Image):   вес 110  ← Низкий
Stream 9 (Tracker): вес 16   ← Минимальный

Оставшаяся проблема HTTP/2

Мультиплексирование решает HTTP-level HOL blocking, но TCP-level HOL blocking остаётся:

TCP-поток:  [Пакет 1][Пакет 2][Пакет X][Пакет 4][Пакет 5]
                                  ↑
                            Потерян пакет 3

Все потоки HTTP/2 ЗАБЛОКИРОВАНЫ, пока TCP не ретрансмитит пакет 3
Даже если пакет 4 и 5 относятся к другим HTTP-потокам!

HTTP/3

QUIC как транспорт

HTTP/3 работает поверх QUIC (UDP) вместо TCP, полностью устраняя TCP-level HOL blocking.

HTTP/2 over TCP:                HTTP/3 over QUIC:
┌───────────┐                   ┌───────────┐
│  HTTP/2   │                   │  HTTP/3   │
├───────────┤                   ├───────────┤
│   TLS 1.3 │                   │   QUIC    │ ← TLS встроен
├───────────┤                   │ (streams) │ ← Независимые потоки
│   TCP     │                   ├───────────┤
├───────────┤                   │   UDP     │
│   IP      │                   ├───────────┤
└───────────┘                   │   IP      │
                                └───────────┘

QPACK: Сжатие заголовков для HTTP/3

QPACK -- адаптация HPACK для QUIC с учётом возможной переупорядоченности пакетов.

HPACK (HTTP/2):                  QPACK (HTTP/3):
Требует строгий порядок          Допускает переупорядоченность

Encoder ──── TCP ────► Decoder   Encoder ── QUIC Stream ──► Decoder
(порядок гарантирован TCP)       (каждый поток независим)

                                 Два специальных потока:
                                 - Encoder Stream (обновления таблицы)
                                 - Decoder Stream (подтверждения)

Установление соединения: сравнение

HTTP/1.1 + TLS 1.2:     3 RTT
  TCP Handshake:  1 RTT  ─────────────────
  TLS 1.2:        2 RTT  ─────────────────
                                           → Данные

HTTP/2 + TLS 1.3:       2 RTT
  TCP Handshake:  1 RTT  ─────────────────
  TLS 1.3:        1 RTT  ─────────────────
                                           → Данные

HTTP/3 (QUIC) первое:   1 RTT
  QUIC + TLS 1.3: 1 RTT ─────────────────
                                           → Данные

HTTP/3 (QUIC) повторное: 0 RTT
  0-RTT:          0 RTT  → Данные сразу!

Connection Migration

Сценарий: пользователь переходит с Wi-Fi на мобильную сеть

HTTP/2 (TCP):
  Wi-Fi:    IP 192.168.1.5   → TCP connection
  Переход:  Соединение разорвано ✗
  Мобильная: IP 10.0.0.1    → Новое TCP-соединение + TLS
                              (2 RTT потеряно)

HTTP/3 (QUIC):
  Wi-Fi:    IP 192.168.1.5   → QUIC Connection ID: 0x1234
  Переход:  Connection ID сохраняется ✓
  Мобильная: IP 10.0.0.1    → Тот же Connection ID: 0x1234
                              (0 RTT, бесшовный переход)

Сравнительная таблица

Характеристика HTTP/1.1 HTTP/2 HTTP/3
Год 1997 2015 2022
Транспорт TCP TCP QUIC (UDP)
Формат Текстовый Бинарный Бинарный
Мультиплексирование Нет (6 соединений) Да (1 соединение) Да (1 соединение)
HOL Blocking HTTP + TCP Только TCP Нет
Сжатие заголовков Нет HPACK QPACK
Server Push Нет Да (deprecated) Нет
Шифрование Опционально Де-факто (TLS) Обязательно (TLS 1.3)
0-RTT Нет Нет Да (повторное)
Connection Migration Нет Нет Да
Adoption (2025) ~30% ~40% ~30%

Практический выбор в System Design

Когда какую версию использовать

Внутренние микросервисы (дата-центр)?
├── gRPC (HTTP/2) — стриминг, schema, производительность
└── HTTP/1.1 — простота, совместимость с legacy

Публичное API для веб-клиентов?
├── HTTP/2 — стандарт, широкая поддержка
└── HTTP/3 — мобильные клиенты, нестабильная сеть

Мобильное приложение?
├── HTTP/3 — connection migration, 0-RTT
└── HTTP/2 — если HTTP/3 не поддерживается

CDN / статика?
└── HTTP/3 + HTTP/2 fallback — максимальная скорость

Оптимизации на уровне HTTP

Техника HTTP/1.1 HTTP/2+
Domain sharding Да (обход лимита 6 conn) Нет (вредит!)
Sprite sheets Да (уменьшение запросов) Не нужно
Inline CSS/JS Да (уменьшение запросов) Не нужно
Bundle JS Да (1 большой файл) Мелкие модули лучше
Resource hints Да (prefetch/preconnect) Да + 103 Early Hints

Проверь себя

Почему domain sharding вредит производительности в HTTP/2?

Почему HTTP/3 использует UDP вместо TCP?

Что такое HPACK и какую проблему он решает?

Сколько RTT нужно для HTTP/3 при повторном соединении (0-RTT resumption)?

Что такое мультиплексирование в HTTP/2 и какую проблему оно решает?