HPACK
HPACK — это стандартизированный метод сжатия заголовков HTTP, разработанный для протокола HTTP/2. Он предназначен для уменьшения избыточности и объёма передаваемых метаданных (заголовков запросов и ответов), что критически важно для повышения производительности веб-приложений, особенно при большом количестве параллельных соединений. HPACK определён в документе RFC 7541 (2015 год) и является обязательным компонентом HTTP/2, в отличие от HTTP/1.1, где сжатие заголовков не было стандартизировано и реализовывалось несовместимыми способами (например, через gzip).
История и предпосылки
До появления HTTP/2 каждый HTTP-запрос и ответ в HTTP/1.1 передавались в виде текстовых строк, включая заголовки. Это приводило к значительному расходу полосы пропускания, особенно на повторяющиеся заголовки (например, Cookie, User-Agent, Accept). В HTTP/1.1 существовала экспериментальная возможность сжатия заголовков (например, с помощью gzip), но она не была стандартизирована и часто приводила к уязвимостям (атаки типа CRIME, BREACH), из-за чего от неё отказались.
При разработке HTTP/2, который базируется на двоичном формате фреймов и мультиплексировании, стало очевидно, что сжатие заголовков необходимо для эффективной работы. Группа IETF HTTPbis, создавшая HTTP/2, разработала HPACK как специализированный алгоритм сжатия, учитывающий особенности HTTP-заголовков: их повторяемость, наличие имён и значений, а также необходимость защиты от атак по сторонним каналам (side-channel attacks).
Принцип работы
HPACK основан на двух ключевых механизмах: статической таблице и динамической таблице, а также на кодировании с помощью кода Хаффмана для значений заголовков.
Статическая таблица
Это предопределённая таблица, содержащая 61 наиболее часто встречающийся заголовок (например, :method: GET, :path: /, content-type, accept-encoding). Каждому заголовку в таблице соответствует индекс. Если заголовок совпадает с записью из статической таблицы, он передаётся не текстом, а одним байтом (индексом). Это позволяет сжимать самые распространённые заголовки практически до нуля.
Динамическая таблица
Это изменяемая таблица, которая строится в процессе обмена данными между клиентом и сервером. Каждый новый заголовок, не найденный в статической таблице, может быть добавлен в динамическую таблицу (при условии, что отправитель указывает это в кодированном представлении). Размер динамической таблицы ограничен (настраивается через параметр SETTINGS_HEADER_TABLE_SIZE, по умолчанию 4096 байт). При переполнении таблицы старые записи вытесняются по принципу LRU (Least Recently Used — наименее недавно использованные).
Динамическая таблица позволяет эффективно сжимать повторяющиеся заголовки в рамках одного соединения. Например, если в одном запросе передаётся длинный заголовок Cookie, он будет отправлен полностью только один раз; в последующих запросах будет передан только его индекс в таблице.
Кодирование Хаффмана
Для значений заголовков, которые не удаётся сжать таблицами (например, длинные строки, уникальные идентификаторы), HPACK использует статический код Хаффмана, оптимизированный для HTTP-трафика. Это позволяет уменьшить размер текстовых значений на 30–50% без потери информации.
Формат кодирования
HPACK оперирует тремя типами инструкций:
- Индексированная запись (Indexed Header Field) — передаётся индекс из статической или динамической таблицы.
- Запись с добавлением в динамическую таблицу (Literal Header Field with Incremental Indexing) — передаётся имя и значение заголовка, которые затем добавляются в динамическую таблицу.
- Запись без добавления в таблицу (Literal Header Field without Indexing) — передаётся имя и значение, но они не кэшируются. Используется для редких или одноразовых заголовков.
- Запись с запретом индексации (Literal Header Field Never Indexed) — аналогично предыдущему, но с явным указанием, что прокси-серверы не должны пытаться кэшировать этот заголовок (например, для конфиденциальных данных).
Преимущества и недостатки
Преимущества
- Значительное снижение объёма трафика: в среднем заголовки сжимаются в 5–10 раз по сравнению с HTTP/1.1. Для повторяющихся запросов (например, загрузка множества ресурсов с одного сайта) выигрыш ещё больше.
- Устойчивость к атакам по сторонним каналам: HPACK не использует динамическое сжатие на основе контекста (как CRIME), а полагается на статические таблицы и кодирование Хаффмана, что делает невозможным утечку данных через длину сжатого вывода.
- Простота реализации: алгоритм не требует сложных вычислений и легко реализуется как на стороне клиента, так и на стороне сервера.
Недостатки
- Зависимость от состояния соединения: динамическая таблица синхронизируется между клиентом и сервером; потеря пакетов или переустановка соединения требуют повторной передачи заголовков.
- Ограниченный размер динамической таблицы: при большом количестве уникальных заголовков таблица быстро переполняется, и эффективность сжатия падает.
- Неэффективность для коротких соединений: при однократных запросах (например, API-вызовы без keep-alive) выигрыш от динамической таблицы минимален.
Применение
HPACK является обязательным для всех реализаций HTTP/2. Он используется в современных веб-браузерах (Chrome, Firefox, Safari, Edge), веб-серверах (nginx, Apache, IIS, Caddy), а также в прокси-серверах и CDN (Cloudflare, Akamai). Без HPACK HTTP/2 был бы значительно менее эффективным, так как мультиплексирование множества потоков привело бы к огромному объёму заголовков.
Кроме того, HPACK оказал влияние на разработку QPACK — алгоритма сжатия заголовков для HTTP/3 (основанного на QUIC). QPACK решает проблему потери пакетов, характерную для ненадёжного транспорта QUIC, путём введения двух типов динамических таблиц (для запросов и для ответов) и механизма кодирования с возможностью восстановления.
Критика и ограничения
Несмотря на эффективность, HPACK критикуется за сложность реализации по сравнению с текстовым форматом HTTP/1.1. Некоторые разработчики отмечают, что динамическая таблица может быть источником ошибок при несинхронизированном состоянии (например, при использовании прокси-серверов, которые модифицируют заголовки). Также HPACK не поддерживает сжатие тела запроса или ответа — для этого используются отдельные алгоритмы (например, gzip, brotli).
Пример работы
Допустим, клиент отправляет запрос: `` :method: GET :path: /index.html :scheme: https :authority: example.com accept-encoding: gzip, deflate user-agent: Mozilla/5.0 ``
В HPACK:
:method: GET— индекс 2 из статической таблицы (1 байт).:path: /index.html— не входит в статическую таблицу, но может быть добавлен в динамическую таблицу (передаётся как литерал с индексированием).:scheme: https— индекс 7 (1 байт).:authority: example.com— может быть добавлен в динамическую таблицу.accept-encoding— индекс 16 (1 байт), значениеgzip, deflate— кодируется Хаффманом.user-agent— не входит в статическую таблицу, передаётся как литерал.
Итоговый размер заголовков в HTTP/2 может составлять 20–30 байт вместо 200–300 байт в HTTP/1.1.
Источники
- RFC 7541: HPACK: Header Compression for HTTP/2 (2015)
- RFC 7540: Hypertext Transfer Protocol Version 2 (HTTP/2) (2015)
- RFC 9204: QPACK: Field Compression for HTTP/3 (2022)
- Документация IETF HTTPbis Working Group
- Статьи и аналитика по производительности HTTP/2 (например, от Cloudflare, nginx)
BFOmetr — база данных и аналитика по компаниям России.
На главную BFOmetr →