ZSTD vs Brotli vs GZIP: выбор сжатия для API
Сравнение ZSTD, Brotli и GZIP для API: скорость, степень сжатия, затраты CPU и практические настройки по умолчанию для JSON и бинарных полезных нагрузок в продакшене.

Что такое сжатие API (и когда оно имеет смысл)
Сжатие ответов API означает, что сервер кодирует тело ответа (часто JSON) в более компактный поток байтов до отправки по сети. Клиент (браузер, мобильное приложение, SDK или другой сервис) затем распаковывает его. В HTTP это оговаривается через заголовки, такие как Accept-Encoding (что поддерживает клиент) и Content-Encoding (что выбрал сервер).
Что это даёт для API
Сжатие в основном приносит три вещи:
- Меньше трафика: меньшие ответы потребляют меньше байтов по всей цепочке.
- Ниже задержка на ограниченных каналах: меньше байтов часто означает быстрее загрузку на мобильных, загруженном Wi‑Fi и при межрегиональных вызовах.
- Ниже расходы на исходящий трафик: если вы платите за исходящий трафик, уменьшение объёма может напрямую снизить счёт.
Компромисс прост: сжатие экономит трафик, но стоит CPU (сжатие/распаковка) и иногда памяти (буферы). Насколько это оправдано — зависит от вашего бутылочного горлышка.
Когда сжатие помогает больше всего
Сжатие особенно эффективно, когда ответы:
- Текстовые и повторяющиеся, например JSON, ответы GraphQL, HTML или логи.
- Среднего или большого размера, когда экономия десятков или сотен килобайт заметна.
- Отдаются по медленным или дорогим сетям, например мобильные клиенты, международные подключения или межрегиональный трафик.
Если вы возвращаете большие списки JSON (каталоги, результаты поиска, аналитику), сжатие часто — один из самых простых выигрышей.
Когда сжатие помогает меньше всего
Сжатие обычно не оправдано CPU, когда ответы:
- Крошечные (несколько сотен байт). Накладные расходы заголовков и CPU могут перевесить выгоды.
- Уже сжаты (JPEG/PNG, MP4, ZIP, многие PDF). Повторное сжатие даёт мало или даже увеличивает размер.
- Сервисы, ограниченные CPU (горячие endpoints с высокой вычислительной нагрузкой). Добавление сжатия может увеличить хвостовую задержку.
Оси принятия решения, которые будут использоваться в руководстве
При выборе между ZSTD vs Brotli vs GZIP практическое решение обычно сводится к трём факторам:
- Сокращение размера (compression ratio)
- Задержка (время сервера до отправки первого байта плюс распаковка на клиенте)
- Поддержка клиентов (что ваши вызывающие и промежуточные звенья надёжно обрабатывают)
Всё остальное в статье — про баланс этих трёх для ваших конкретных API и паттернов трафика.
ZSTD vs Brotli vs GZIP: краткое сравнение
Все три уменьшают размер полезной нагрузки, но оптимизируют разные параметры — скорость, степень сжатия и совместимость.
Краткий обзор
- ZSTD (Zstandard): часто лучший баланс для API, когда важна низкая задержка и предсказуемый CPU. Хорошая степень сжатия без большой замедленности.
- Brotli: часто выигрывает по минимальному объёму по сети, особенно для текстовых ответов (JSON, HTML‑подобный контент). Высокие уровни могут стоить дороже по CPU.
- GZIP: «работает везде». Широко поддерживается и легко эксплуатируется, но обычно медленнее и/или даёт больший размер, чем современные альтернативы при сопоставимых затратах CPU.
Типичные сильные стороны (и что это значит для API)
Скорость ZSTD: отлично подходит, когда API чувствительны к хвостовой задержке или сервера ограничены по CPU. Он может сжимать достаточно быстро, чтобы накладные расходы часто были несущественны по сравнению с сетевым временем — особенно для ответов среднего и большого размера.
Степень сжатия Brotli: выигрывает, когда приоритет — минимум байт (мобильные клиенты, дорогой egress, доставка через CDN) и ответы в основном текстовые. Меньшие полезные нагрузки могут оправдать более долгую компрессию.
Совместимость GZIP: подходит, когда нужна максимальная поддержка клиентов с минимальным риском переговоров (старые SDK, встраиваемые клиенты, устаревшие прокси). Это безопасная база, даже если она не лучшая по производительности.
Что реально меняет «уровень сжатия»
«Уровни» сжатия — это пресеты, торгующие временем CPU за меньший выходной размер:
- Низкие уровни: быстрее сжатие, большие полезные нагрузки. Хорошо для реал‑тайм API.
- Высокие уровни: меньше размер, медленнее сжатие (и иногда больше памяти). Лучше для больших, кешируемых ответов.
Распаковка обычно гораздо дешевле, чем сжатие для всех трёх форматов, но очень высокие уровни всё ещё могут увеличить нагрузку на клиент и батарею — важно для мобильных.
Простое правило
- Выбор по умолчанию: используйте ZSTD для большинства JSON/REST/GraphQL API, где важна задержка.
- Перейдите на Brotli: когда оптимизируете минимум байт (текстовые ответы, доставка через CDN, медленные сети) и можете позволить себе больше CPU.
- Оставайтесь на GZIP: когда нужна широкая совместимость или инфраструктура/инструменты не поддерживают новые кодеки.
Соотношение степени сжатия и задержки: главный компромисс
Сжатие часто рекламируют как «меньше байтов = быстрее API». Это верно в сетях с низкой пропускной способностью — но не автоматически. Если сжатие добавляет достаточно серверного CPU‑времени, вы можете получить более медленные ответы, несмотря на меньший объём в сети.
Куда уходит время
Полезно разделить затраты на две части:
- Время сжатия (на сервере): работа до того, как сервер может начать отправлять байты. Это прямо добавляет к времени ответа (TTFB).
- Время распаковки (на клиенте): работа после получения байтов. Обычно дешевле, чем сжатие, но может иметь значение на слабых устройствах.
Высокая степень сжатия может сократить время передачи, но если сжатие добавляет, скажем, 15–30 мс CPU на ответ, вы можете потерять больше времени, чем сэкономите — особенно на быстрых соединениях.
Ловушка хвостовой задержки под нагрузкой
Под нагрузкой сжатие может ухудшать p95/p99 сильнее, чем p50. Когда CPU загружается, запросы встают в очередь. Очереди усиливают небольшие накладные расходы на запрос, превращая их в большие задержки — средняя задержка может выглядеть нормально, а вот самые медленные пользователи пострадают.
Измеряйте как фичу производительности
Не догадывайтесь. Запустите A/B‑тест или постепенный релиз и сравните:
- p50 и p95 latency (и желательно p99)
- Загрузка CPU и насыщение на инстансах API
- Размеры ответов и time-to-first-byte
Тестируйте с реалистичными паттернами трафика и полезными нагрузками. «Лучший» уровень сжатия — тот, который уменьшает суммарное время, а не только байты.
Затраты CPU и памяти на сервере и клиенте
Сжатие не бесплатно — оно переносит работу из сети в CPU и память на обоих концах. В API это проявляется как большее время обработки запроса, больший объём памяти и иногда замедление на стороне клиента.
Куда уходит CPU
Большая часть CPU уходит на сжатие ответов. Сжатие ищет повторяющиеся паттерны, строит состояния/словаря и пишет закодированный выход.
Распаковка обычно дешевле, но всё ещё важна:
- Серверы могут распаковывать запросы (редко для JSON API, чаще для загрузок или батчированных событий).
- Клиенты распаковывают ответы на критическом пути перед парсингом JSON.
Если ваш API уже нагружен по CPU (дорогие запросы, сложная аутентификация, тяжёлые вычисления), включение высокого уровня сжатия может повысить хвостовые задержки даже при уменьшении полезной нагрузки.
Память
Сжатие может увеличить использование памяти:
- Буферы: реализации могут требовать входные/выходные буферы; большие ответы — большие буферы.
- Полное буферизование против стриминга: стриминговое сжатие может начать отправлять раньше и держать память более плоской, тогда как полное буферизование увеличивает пик памяти на запрос.
В контейнеризованных окружениях большие пики памяти приводят к OOM и снижению плотности инстансов.
Влияние на автоскейлинг и лимиты контейнеров
Сжатие добавляет CPU‑циклы на ответ, уменьшая пропускную способность на инстанс. Это может ускорить масштабирование и повысить затраты. Распространённый паттерн: трафик в байтах падает, но расход CPU растёт — поэтому правильный выбор зависит от того, какой ресурс у вас в дефиците.
Почему скорость распаковки важна для клиентов
На мобильных или слабых устройствах распаковка конкурирует с рендерингом, выполнением JavaScript и зарядом батареи. Формат, который экономит несколько килобайт, но дольше распаковывается, может ощущаться медленнее, особенно когда важно «время до доступных данных».
ZSTD для API: сильные стороны, ограничения и хорошие настройки по умолчанию
Zstandard (ZSTD) — современный формат сжатия, спроектированный так, чтобы давать хорошую степень сжатия без замедления API. Для многих JSON‑heavy API он — крепкий «дефолт»: заметно меньшие ответы, чем GZIP, при сопоставимой или меньшей задержке, плюс очень быстрая распаковка на клиентах.
Для чего ZSTD особенно хорош
ZSTD ценен, когда важен end‑to‑end time, а не только минимальный объём. Он обычно сжимает быстро и распаковывает чрезвычайно быстро — полезно там, где каждая миллисекунда CPU конкурирует с обработкой запросов.
Он также хорошо показывает себя на широком диапазоне размеров полезной нагрузки: небольшие и средние JSON получают ощутимые выигрыши, а большие ответы — ещё больше выгоды.
Разумные уровни сжатия для API
Для большинства API начинайте с низких уровней (обычно 1–3). Они часто дают наилучшее соотношение латентность/размер.
Повышайте уровень только когда:
- Полезные нагрузки большие (сотни КБ — МБ)
- Трафик дорогой или ограниченный
- Вы измерили и увидели, что CPU не узкое место
Прагматичный подход — низкий глобальный дефолт и выборочное повышение уровня для «больших» endpoint'ов.
Стриминг и режим словаря
ZSTD поддерживает стриминг, что уменьшает пик памяти и позволяет начинать отправку раньше для больших ответов.
Режим словаря может дать большой выигрыш для API, возвращающих много похожих объектов (повторяющиеся ключи, стабильные схемы). Это особенно эффективно, когда:
- Полезные нагрузки относительно небольшие, но частые
- Вы можете безопасно управлять версионированными словарями
Ограничения совместимости
На стороне сервера поддержка простая во многих стеках, но совместимость клиентов может быть решающим фактором. Некоторые HTTP‑клиенты, прокси и шлюзы всё ещё не объявляют или не принимают Content-Encoding: zstd по умолчанию.
Если вы обслуживаете сторонних потребителей, держите fallback (обычно GZIP) и включайте ZSTD только когда Accept-Encoding явно его включает.
Brotli для API: когда он выигрывает, а когда — нет
Brotli создан для максимальной экономии на текстовом контенте. Для JSON, HTML и других «словесных» payload'ов он часто обгоняет GZIP по степени сжатия — особенно на высоких уровнях.
Где Brotli выигрывает
Текстовые ответы — сильная сторона Brotli. Если ваш API отправляет большие JSON‑документы (каталоги, результаты поиска, конфигурационные блобы), Brotli может заметно сократить байты, что помогает в медленных сетях и снижает расходы на egress.
Brotli также выгоден, когда вы можете сжать один раз и отдавать множество раз ( кешируемые ответы, версионированные ресурсы). В таких случаях высокие уровни Brotli окупаются, поскольку стоимость CPU амортизируется.
Где Brotli разочаровывает
Для динамических ответов (генерируемых на каждый запрос) лучшие соотношения Brotli часто требуют высоких уровней, которые могут быть ресурсоёмкими и добавлять задержку. Учитывая время сжатия, реальная выгода над ZSTD (или хорошо настроенным GZIP) может быть меньше, чем кажется на бумаге.
Он также менее полезен для payload'ов, которые плохо сжимаются (уже сжатые данные, многие бинарные форматы). В таких случаях вы просто тратите CPU.
Практические рекомендации по уровням
- Runtime‑сжатие: используйте низкие уровни (обычно 1–4), чтобы избежать всплесков CPU.
- Предсжатый/статический контент: уровни 8–11 оправданы, когда стоимость CPU амортизируется многими запросами.
Примечания по поддержке клиентов
Браузеры обычно хорошо поддерживают Brotli по HTTPS, поэтому он популярен для веб‑трафика. Для небраузерных API клиентов (мобильные SDK, IoT, старые HTTP‑стэки) поддержка может быть непостоянной — поэтому корректно оговаривайте через Accept-Encoding и держите fallback (обычно GZIP).
GZIP для API: совместимость и практическая производительность
GZIP остаётся стандартным ответом для сжатия API, потому что это самый универсальный вариант. Практически каждый HTTP‑клиент, браузер, прокси и шлюз понимает Content-Encoding: gzip, и эта предсказуемость важна, когда вы не полностью контролируете промежуточные звенья.
Почему он всё ещё распространён
Преимущество не в том, что GZIP «лучший», а в том, что он редко оказывается неправильным выбором. Многие команды имеют годы операций с ним, чувствительные дефолты в веб‑серверах и меньше сюрпризов с промежуточными устройствами, которые могут некорректно обращаться с новыми кодеками.
Практические уровни сжатия для API
Для API‑полезных нагрузок (часто JSON) средние‑низкие уровни — золотая середина. Уровни 1–6 обычно дают большую часть уменьшения размера при разумном CPU‑потреблении.
Очень высокие уровни (8–9) могут выжать немного больше, но затраты CPU редко оправдывают это для динамичного трафика, где важна задержка.
Сравнение на современных CPU
На современном железе GZIP обычно медленнее, чем ZSTD при сопоставимом уровне сжатия, и часто не может дотянуться до лучших показателей Brotli на текстовых payload'ах. На практике это означает:
- ZSTD часто выигрывает по скорости на сохранённый байт.
- Brotli может побеждать по размеру для сильно сжимаемого текста, но может стоить дороже по CPU.
- GZIP остаётся конкурентоспособным, потому что он «достаточно быстрый» и хорошо оптимизирован во многих стеках.
Краевые случаи совместимости
Если нужно поддержать старые клиенты, встраиваемые устройства, корпоративные прокси или легаси‑шлюзы, GZIP — самый безопасный выбор. Некоторые промежуточные устройства будут удалять неизвестные кодировки, ломать переговоры или не пропускать их — чего гораздо меньше с GZIP.
Если среда у вас смешанная, лучше начать с GZIP и добавить ZSTD/Brotli там, где вы контролируете полный путь.
Типы полезной нагрузки: что хорошо сжимается (а что нет)
Выигрыш от сжатия зависит не только от алгоритма, но и от типа данных. Некоторые payload'ы резко уменьшаются с ZSTD/Brotli/GZIP; другие едва меняются и просто сжигают CPU.
Отличные кандидаты (высокая отдача)
Текстовые ответы обычно очень хорошо сжимаются из‑за повторяющихся ключей, пробелов и предсказуемых паттернов.
- JSON (типичные REST‑ответы)
- GraphQL (часто многословные с повторяющимися именами полей)
- XML и HTML
- Большие plain‑text логи и трассы ошибок, возвращаемые API
Чем больше повторяемости и структуры — тем лучше степень сжатия.
Бинарные payload'ы: «возможно» (измеряйте сначала)
Бинарные форматы вроде Protocol Buffers и MessagePack компактнее JSON, но не случайны. Они могут содержать повторяющиеся теги, похожие макеты записей и предсказуемые последовательности.
Это значит, что они часто всё ещё сжимаются, особенно для больших ответов или endpoint'ов со списками. Единственный надёжный ответ — тестировать с реальным трафиком: тот же endpoint, те же данные, сжатие вкл/выкл и сравнить и размер, и задержку.
Обычно не стоит сжимать (уже сжато)
Многие форматы уже используют внутреннее сжатие. Применение HTTP‑сжатия сверху даёт малую экономию и может увеличить время ответа.
- Изображения: JPEG, PNG, WebP
- Видео/аудио: MP4 (и подобные)
- Архивы: ZIP, gzip файлы
- PDF: часто уже содержит сжатие
Для них обычно отключают сжатие по типу контента.
Практические эвристики (простота важнее)
Простой подход — сжимать только когда ответы превышают минимальный размер:
- Установите минимальный порог размера ответа (например, несколько KB) перед включением
Content-Encoding. - Всегда сжимайте большие текстовые ответы; пропускайте сжатие для маленьких JSON, где заголовки доминируют.
Это позволяет CPU работать там, где сжатие действительно снижает трафик и улучшает end‑to‑end производительность.
HTTP‑заголовки и переговоры: как сделать правильно
Сжатие работает плавно, когда клиент и сервер соглашаются на кодировку. Это делается через Accept-Encoding (от клиента) и Content-Encoding (от сервера).
Accept-Encoding и Content-Encoding (простые примеры)
Клиент объявляет, что он может декодировать:
GET /v1/orders HTTP/1.1
Host: api.example
Accept-Encoding: zstd, br, gzip
Сервер выбирает одну и указывает, что использовал:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Encoding: zstd
Если клиент отправил Accept-Encoding: gzip, а вы ответили Content-Encoding: br, этот клиент может не распарсить тело. Если клиент не отправляет Accept-Encoding, безопасный дефолт — не сжимать.
Приоритет серверной стороны
Практический порядок для API часто такой:
zstdсначала- затем
br - затем
gzip
Другими словами: zstd > br > gzip.
Не делайте это догмой: если ваш трафик в основном — браузеры, br может иметь более высокий приоритет; если у вас старые мобильные клиенты, gzip может быть самым надёжным выбором.
Vary: Accept-Encoding и кэширование
Если ответ может быть отдан в нескольких кодировках, добавляйте:
Vary: Accept-Encoding
Без этого CDN или прокси может закешировать gzip‑версию и неправильно отдать её клиенту, который не просил (или не умеет) эту кодировку.
Краевые случаи и безопасные fallback'ы
Некоторые клиенты заявляют поддержку, но имеют баги в декодерах. Чтобы быть устойчивым:
- Отдавайте известный надёжный fallback: если ошибки декодирования растут для
zstd, временно откатывайтесь наgzip. - Рассмотрите allowlist проблемных user‑agent'ов или версий SDK.
- Для критичных endpoints (аутентификация, webhooks) можно отключить сжатие или использовать только наиболее совместимый вариант.
Переговоры — это скорее про отсутствие поломок клиентов, чем про экономию каждого байта.
HTTP/2, HTTP/3, CDN и шлюзы
Сжатие API не существует в вакууме. Ваш транспортный протокол, накладные расходы TLS и любые CDN/шлюзы между ними могут изменить реальный результат — или даже поломать поведение при неверной конфигурации.
HTTP/2 и HTTP/3: мультиплексирование, head‑of‑line и что меняет сжатие
В HTTP/2 несколько запросов разделяют одно TCP‑соединение. Это уменьшает накладные расходы на установку соединения, но потеря пакета может остановить все потоки из‑за TCP head‑of‑line blocking. Сжатие помогает, уменьшая объём ответов, снижая количество данных, «заблокированных» за событием потери пакета.
HTTP/3 работает поверх QUIC (UDP) и избегает TCP‑уровневого head‑of‑line между потоками. Размер полезной нагрузки всё ещё важен, но штраф за потерю пакета на поток обычно меньше. На практике сжатие остаётся полезным — ожидайте выгоды в виде экономии пропускной способности и более быстрой «time to last byte», чем драматических падений латентности.
TLS: не забывайте про бюджет CPU
TLS уже потребляет CPU (хендшейки, шифрование/дешифрование). Добавление сжатия (особенно на высоких уровнях) может вывести вас за пределы CPU при всплесках. Поэтому «быстрое сжатие с хорошим соотношением» часто превосходит «максимальную степень сжатия» в продакшне.
CDN и API‑шлюзы: авто‑сжатие, passthrough или удаление
Некоторые CDN/шлюзы автоматически сжимают определённые MIME‑типы, другие пропускают то, что приходит от origin. Некоторые могут нормализовать или даже убрать Content-Encoding, если настроены неверно.
Проверьте поведение для каждого маршрута и убедитесь, что Vary: Accept-Encoding сохраняется, чтобы кэши не отдали сжатый вариант клиенту, который этого не просил.
Стратегия кеширования: edge vs origin (и мультиварианты)
Если вы кешируете на краю, рассмотрите хранение отдельных вариантов для каждой кодировки (gzip/br/zstd), вместо перекомпрессии при каждом хите. Если кеш на origin, вы всё равно можете захотеть, чтобы edge договаривался и кешировал несколько кодировок.
Ключ — согласованность: корректный Content-Encoding, правильный Vary и чёткое разграничение, где происходит сжатие.
Рекомендуемые дефолты и план настройки
Предложения по умолчанию по сценарию
Для API, ориентированных на браузеры, предпочитайте Brotli, если клиент объявляет его (Accept-Encoding: br). Браузеры эффективно декодируют Brotli, и он чаще даёт лучшую экономию на текстовых ответах.
Для внутренних сервисов (service‑to‑service) используйте ZSTD, когда обе стороны под вашим контролем. Он обычно быстрее при схожих или лучших соотношениях, чем GZIP, и переговоры просты.
Для публичных API, используемых разными SDK, держите GZIP как универсальный базовый вариант и опционно добавляйте ZSTD как опцию для клиентов, которые явно её поддерживают. Это избегает ломки старых HTTP‑стэков.
Консервативные стартовые уровни
Начните с уровней, которые легко измерить и которые вряд ли удивят вас:
- Brotli: уровень 4–6 для динамических ответов (более высокие уровни могут заметно увеличить CPU)
- ZSTD: уровень 3–5 для универсальных API‑payload'ов
- GZIP: уровень 5–6 (более высокие уровни редко окупаются)
Если вам нужна сильнее степень сжатия, валидируйте на выборке продакшен‑payload'ов и отслеживайте p95/p99 перед повышением уровней.
Минимальные пороги размера
Сжатие крошечных ответов может стоить больше CPU, чем экономит в сети. Практическая стартовая точка:
- Не сжимать ниже 1–2 KB для большинства API
- Рассмотрите 4 KB, если вы ограничены по CPU или ответы очень «болтливы»
Настраивайте, сравнивая: (1) сохранённые байты, (2) добавленное серверное время, (3) изменение end‑to‑end latency.
Безопасные способы открытия управления
Разворачивайте сжатие за feature flag, затем добавьте пер‑маршрутную конфигурацию (включать для /v1/search, отключать для уже маленьких endpoint'ов). Предоставьте опцию отключения на клиенте через Accept-Encoding: identity для отладки и слабых клиентов. Всегда включайте Vary: Accept-Encoding, чтобы кэши оставались корректными.
Где это проявляется в современных workflow'ах
Если вы быстро генерируете API (например, фронтенды на React с бэкендом на Go + PostgreSQL), сжатие становится той самой «маленькой настройкой с большим эффектом». На Koder.ai команды часто доходят до этого раньше, потому что быстро прототипируют и деплоят полные приложения, затем точечно настраивают поведение в продакшене (включая сжатие ответов и заголовки кэша). Вывод: относитесь к сжатию как к фиче производительности, релизьте за флагом и меряйте p95/p99.
Развёртывание, мониторинг и отладка
Изменения в сжатии легко выкатывать и удивительно легко ошибиться. Обращайтесь к этому как к продакшен‑фиче: запускайте поэтапно, измеряйте влияние и держите простой откат.
Безопасный план развёртывания
Начните с канарки: включите новый Content-Encoding (например, zstd) для небольшой части трафика или одного внутреннего клиента.
Затем плавно наращивайте (1% → 5% → 25% → 50% → 100%), останавливаясь при отрицательной динамике ключевых метрик.
Держите простой откат:
- Фича‑флаг на gateway/сервисе для отключения сжатия (или отката на
gzip) - Способ исключить конкретные endpoint'ы (скачивания файлов, уже сжатые медиа)
- Быстрый конфиг‑деплой, а не код‑деплой
Что мониторить (и почему)
Отслеживайте сжатие как изменение производительности и надёжности:
- CPU (сервер и, если возможно, клиент): высокие уровни сжатия могут вызвать всплески CPU
- Latency перцентили (p50/p95/p99): сжатие часто помогает средней латентности, но может навредить хвостовой
- Размеры ответов: байты по сети по endpoint'ам, плюс дельта «сжатый vs несжатый»
- Ошибки: 4xx/5xx, ошибки декодирования у клиентов и таймауты
Чек‑лист отладки
Когда что‑то ломается, обычные причины:
- Двойное сжатие: upstream уже сжал, затем gateway сжал снова
- Неверные заголовки:
Content-Encodingуказан, но тело не сжато (или наоборот) - Плохой переговор: игнорируется
Accept-Encoding, или возвращается кодировка, которую клиент не объявлял - Повреждённые потоки: усечённые тела, неправильный
Content-Lengthили вмешательство прокси/CDN
Документируйте ожидания клиентов
Опишите поддерживаемые кодировки в документации, с примерами:
- Что клиенты должны отправлять:
Accept-Encoding: zstd, br, gzip - Что они получат:
Content-Encoding: zstd(или fallback)
Если вы поставляете SDK, добавьте короткие примеры декодирования и укажите минимальные версии, которые поддерживают Brotli или Zstandard.
FAQ
Когда действительно стоит включать сжатие ответов API?
Используйте сжатие ответов, когда ответы богаты текстом (JSON/GraphQL/XML/HTML), средние или большие по размеру, и ваши пользователи находятся в медленных/дорогих сетях или вы платите значимые затраты на исходящий трафик. Пропускайте (или ставьте высокий порог) для крошечных ответов, уже сжатых медиа (JPEG/MP4/ZIP/PDF) и CPU‑ограниченных сервисов, где дополнительная работа на запрос ухудшит p95/p99.
Почему сжатие может сделать API медленнее, хотя ответы стали меньше?
Потому что это обменивает пропускную способность на CPU (и иногда память). Время на сжатие может отложить момент начала отправки байтов (TTFB), а под нагрузкой добавочная нагрузка CPU вызывает очереди—это часто ухудшает хвостовые задержки даже если средняя задержка уменьшается. Лучшие настройки — те, которые минимизируют конечное время запроса, а не только размер полезной нагрузки.
Как выбрать между ZSTD, Brotli и GZIP?
Практический приоритет для многих API:
zstdсначала (быстро, хорошее соотношение)- затем
br(часто наиболее компактно для текста, может требовать больше CPU) - затем
gzip(наибольшая совместимость)
Всегда опирайтесь на то, что клиент объявляет в Accept-Encoding, и держите безопасный запасной вариант (обычно gzip или identity).
Какие уровни сжатия sensible для динамических ответов API?
Начинайте с низких уровней и измеряйте.
- ZSTD: уровень 1–3 (или до 3–5) для большинства динамических JSON API
- Brotli: уровень 1–4 для сжатия в рантайме; уровни 8–11 — для предсжатого/статического контента
- GZIP: уровень 5–6 как хороший дефолт
Более высокие уровни обычно дают убывающую отдачу по размеру, но могут резко повысить CPU и ухудшить p95/p99.
Стоит ли сжимать каждый ответ или только выше определённого размера?
Используйте порог минимального размера ответа, чтобы не тратить CPU на крошечные полезные нагрузки.
- Типичная стартовая точка: 1–2 KB
- Если вы ограничены по CPU или очень «болтливы»: рассмотрите 4 KB
Тонкая настройка на уровне endpoint: сравнивайте сэкономленные байты против добавленного серверного времени и влияния на p50/p95/p99.
Какие типы полезной нагрузки хорошо сжимаются (а какие обычно нет)?
Сосредоточьтесь на типах контента, которые структурированы и повторяются:
- Отлично: JSON, GraphQL, XML, HTML, большие текстовые логи
- «Может быть»: Protobuf/MessagePack (часто всё ещё сжимаются — измеряйте)
- Обычно не стоит: JPEG/PNG/WebP, MP4, ZIP/gz, многие PDF
Обычный подход — включать сжатие только для текстоподобных Content-Type и отключать для известных уже сжатых форматов.
Как работают Accept-Encoding и Content-Encoding для API?
Сжатие должно следовать HTTP‑переговору:
- Клиент отправляет
Accept-Encoding(например,zstd, br, gzip) - Сервер отвечает поддерживаемым
Content-Encoding
Если клиент не отправляет Accept-Encoding, самым безопасным ответом обычно является отсутствие сжатия. Никогда не возвращайте Content-Encoding, которого клиент не объявлял, иначе клиент может не распарсить тело.
Почему `Vary: Accept-Encoding` важен при использовании сжатия?
Добавьте заголовок:
Vary: Accept-Encoding
Это предотвращает ситуацию, когда CDN/прокси кэширует, например, gzip‑версию и ошибочно отдает её клиенту, который не запросил или не умеет декодировать gzip (или zstd/br). Если вы поддерживаете несколько кодировок, этот заголовок обязателен для корректного кэширования.
Какие наиболее распространённые баги при использовании сжатия в продакшене?
Частые ошибки:
- Двойное сжатие (origin сжимает, потом gateway/CDN сжимает снова)
- Несоответствие заголовок/тело (
Content-Encodingговорит gzip, а тело не gzip) - Плохой переговор (игнорирование
Accept-Encoding) - Вмешательство прокси/CDN (удаление или изменение заголовков)
- Неправильный
Content-Lengthпри стриминге/сжатии
При отладке захватывайте необработанные заголовки ответа и проверяйте декомпрессию известным рабочим клиентом/инструментом.
Как безопасно раскатывать и мониторить сжатие API?
Разворачивайте как фичу производительности:
- Канар: включите новый
Content-Encoding(например,zstd) для небольшой доли трафика - Затем постепенно наращивайте (например, 1% → 5% → 25% → 50% → 100%), останавливаясь при отклонении ключевых метрик
Держите быстрый откат (фича‑флаг или конфиг gateway), и мониторьте:
- CPU (использование/сателлитация)
- p50/p95/p99 latency и TTFB
- wire bytes (сжатые против несжатых)
- ошибки/таймауты и ошибки декодирования на клиентах
Если хвостовые задержки растут под нагрузкой — понижайте уровень, повышайте порог или переключайтесь на более быстрый кодек (часто ZSTD).