Gzip и Brotli: сжатие ответов веб-сервера
Как работает HTTP-сжатие, какие директивы gzip реально нужны в nginx, чем Brotli отличается от gzip и превосходит его, как предсжимать статику заранее и в каких случаях сжатие только вредит.
Как работает сжатие ответов в HTTP
HTTP-сжатие — это механизм, при котором сервер уменьшает размер тела ответа перед отправкой клиенту, используя один из согласованных алгоритмов сжатия, а браузер (или другой HTTP-клиент) распаковывает полученные данные обратно перед тем, как использовать их. Согласование алгоритма происходит через заголовки запроса и ответа: клиент присылает Accept-Encoding со списком поддерживаемых им алгоритмов сжатия в порядке предпочтения, а сервер выбирает из этого списка тот, который умеет использовать сам, и указывает выбранный вариант в заголовке ответа Content-Encoding.
Accept-Encoding: gzip, deflate, br
Content-Encoding: br
Практическая ценность сжатия для сайта прямая и легко измеримая: текстовые ресурсы — HTML-разметка, CSS-стили, JavaScript-код, JSON-ответы API — обычно сжимаются в несколько раз от исходного размера, поскольку содержат большое количество повторяющихся последовательностей символов: отступы, одинаковые имена тегов и атрибутов, повторяющиеся ключевые слова языка программирования, схожие структуры в объектах JSON. Меньший объём передаваемых данных означает более быструю загрузку страницы, особенно заметную на медленных или нестабильных мобильных соединениях.
Сжатие происходит на уровне тела ответа целиком, а не отдельных его частей, и полностью прозрачно для конечного приложения — веб-сервер или обратный прокси сжимает уже сформированный ответ перед отправкой в сеть, и ни бэкенду, ни фронтенд-коду не нужно ничего знать о том, что данные были сжаты по дороге. Это делает включение сжатия одной из немногих оптимизаций производительности, которая внедряется исключительно на уровне конфигурации сервера, без единой строчки изменений в самом приложении.
Gzip в nginx: ключевые директивы
Nginx умеет сжимать ответы алгоритмом gzip через встроенный модуль ngx_http_gzip_module, доступный в стандартной сборке без дополнительной установки. Базовая настройка требует всего нескольких директив в конфигурации сервера.
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript;
gzip_min_length 1024;
gzip_comp_level 6;
gzip_vary on;
Директива gzip_types явно перечисляет MIME-типы, для которых будет применяться сжатие, — по умолчанию nginx сжимает только text/html, поэтому без явного расширения этого списка все остальные текстовые ресурсы (CSS, JavaScript, JSON) будут отдаваться несжатыми даже при включённом gzip on. Список стоит ограничивать текстовыми и около-текстовыми форматами, поскольку сжатие бинарных уже сжатых форматов не даёт практической пользы, о чём подробнее ниже.
gzip_min_length устанавливает минимальный размер ответа в байтах, ниже которого сжатие не применяется вовсе, — для совсем маленьких ответов накладные расходы на сжатие (заголовки алгоритма, время на само сжатие) могут превысить выигрыш от уменьшения размера тела, поэтому есть смысл не тратить на них CPU впустую. gzip_comp_level задаёт уровень сжатия от 1 (быстрее, слабее сжимает) до 9 (медленнее, сильнее сжимает) — для сжатия на лету под реальной нагрузкой практика склоняется к среднему значению 5-6, поскольку более высокие уровни дают убывающий прирост в размере ценой заметно возросшей нагрузки на процессор.
Директива gzip_vary добавляет в ответ заголовок Vary: Accept-Encoding, сообщающий промежуточным кэширующим прокси и CDN, что содержимое ответа зависит от заголовка Accept-Encoding запроса, — без этого заголовка кэширующий узел рискует один раз закэшировать сжатый ответ и затем ошибочно отдать его клиенту, который сжатие не поддерживает, либо наоборот.
Brotli: более современная альтернатива
Brotli — алгоритм сжатия, разработанный Google и опубликованный в 2015 году, спроектированный специально с учётом типичного содержимого веб-страниц. Его ключевое отличие от gzip — встроенный статический словарь из примерно 13 тысяч часто встречающихся в вебе последовательностей: общеупотребительных английских слов, характерных HTML-тегов, CSS-свойств и типичных конструкций JavaScript, которые Brotli распознаёт и заменяет короткими ссылками на словарь, даже если конкретный файл сам по себе эти последовательности не повторяет много раз.
На практике для типичных веб-ресурсов — HTML, CSS, JavaScript — Brotli на сопоставимом уровне сжатия обычно даёт заметно более компактный результат, чем gzip, при сопоставимой или даже более быстрой скорости распаковки на стороне клиента. Обратная сторона — сжатие данных алгоритмом Brotli на высоких уровнях качества заметно медленнее, чем gzip, что делает Brotli менее подходящим для сжатия на лету динамического контента под высокой нагрузкой и куда более подходящим для предварительного сжатия статических файлов, где время сжатия не имеет значения.
# Требует модуля ngx_brotli, собранного отдельно или подключённого динамически
brotli on;
brotli_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript;
brotli_comp_level 5;
Важная техническая деталь: в отличие от gzip, поддержка Brotli не встроена в стандартную сборку nginx по умолчанию — требуется отдельный модуль ngx_brotli, который либо собирается вместе с nginx из исходников, либо подключается как динамический модуль, если используемая сборка nginx это поддерживает (многие современные пакеты для популярных дистрибутивов уже включают такую возможность). Проверить наличие модуля можно командой nginx -V и поиском brotli в выведенном списке параметров сборки.
nginx -V 2>&1 | grep -o brotli
На практике обе директивы — gzip и brotli — включают одновременно, а не выбирают что-то одно: nginx сам выберет подходящий алгоритм на основе заголовка Accept-Encoding конкретного запроса, отдавая Brotli тем клиентам, что его поддерживают (подавляющее большинство современных браузеров), и откатываясь на gzip для более старых или нетипичных клиентов, которые Brotli не заявляют в своих заголовках.
Предварительное сжатие статических файлов
Сжатие на лету при каждом запросе — не единственный и часто не лучший подход для статических файлов, которые не меняются между запросами: CSS, JavaScript-бандлы, собранные при деплое, шрифты в текстовых форматах. Для них разумнее сжать файл один раз заранее (на этапе сборки проекта или прямо во время деплоя) и сохранить результат рядом с оригиналом, а не тратить CPU сервера на повторное сжатие одного и того же содержимого при каждом обращении.
gzip -9 -k -f app.bundle.js
brotli -q 11 -k -f app.bundle.js
ls app.bundle.js*
Флаг -k у обеих утилит сохраняет исходный несжатый файл рядом с результатом (по умолчанию некоторые реализации удаляют оригинал), а максимальные уровни сжатия — gzip -9 и brotli -q 11 — оправданы именно здесь, поскольку сжатие выполняется один раз при сборке, а не на каждый запрос под нагрузкой, и время выполнения этой команды не влияет на скорость ответа реальным пользователям сайта.
location ~* \.(css|js)$ {
gzip_static on;
brotli_static on;
}
Директивы gzip_static и brotli_static указывают nginx проверять наличие рядом с запрошенным файлом уже сжатой версии (с расширением .gz или .br соответственно) и, если она найдена, отдавать её напрямую вместо повторного сжатия оригинала на лету, — это полностью снимает нагрузку на CPU сервера в момент обработки запроса и позволяет использовать максимальный уровень сжатия без каких-либо компромиссов по скорости ответа. gzip_static — часть стандартной сборки nginx, тогда как brotli_static идёт в том же модуле ngx_brotli, что и обычный Brotli.
Для проектов со сборкой через современные фронтенд-инструменты (Webpack, Vite, esbuild) предварительное сжатие обычно добавляется отдельным плагином прямо в процесс сборки, генерирующим .gz и .br файлы автоматически рядом с каждым итоговым бандлом, — это избавляет от необходимости запускать сжатие отдельным шагом вручную при каждом деплое.
Когда сжатие не помогает и даже вредит
Форматы, уже использующие собственное сжатие внутри своей структуры — JPEG, PNG (с оптимальным уровнем компрессии), MP4, WebP, ZIP-архивы, — почти не выигрывают от дополнительного прогона через gzip или Brotli, поскольку данные внутри них уже имеют высокую энтропию и мало повторяющихся последовательностей, которые могли бы найти и заменить алгоритмы сжатия. Попытка сжать такие файлы дополнительно обычно даёт выигрыш на уровне долей процента, а иногда даже незначительно увеличивает итоговый размер из-за служебных заголовков формата сжатия.
Это прямое практическое следствие для директивы gzip_types: явно перечисляя MIME-типы для сжатия, стоит намеренно не включать в список image/jpeg, image/png, video/mp4 и аналогичные уже сжатые бинарные форматы — трата CPU на их повторное сжатие не окупается практически никаким выигрышем в размере передаваемых данных для конечного пользователя.
Второй практический риск — сжатие на лету небольших, но очень часто запрашиваемых ответов без учёта соотношения затрат и выгоды. Для маленького JSON-ответа API размером в пару сотен байт время, потраченное CPU сервера на сжатие при каждом запросе, может оказаться сопоставимым с временем передачи самого несжатого ответа по быстрому каналу связи — здесь как раз и работает директива gzip_min_length, отсекающая совсем маленькие ответы от бессмысленного сжатия.
Отдельная историческая проблема — уязвимость BREACH, затрагивающая сжатие ответов, содержащих одновременно секретные данные (например, CSRF-токен) и отражённый пользовательский ввод в рамках одного HTTPS-соединения: анализируя изменение размера сжатого ответа при переборе символов во вводе, атакующий теоретически может по крупицам восстановить секретное значение. На практике риск актуален для довольно специфичных сценариев с прямым отражением ввода рядом с секретом в одном ответе, но при разработке чувствительных к утечке данных эндпоинтов эту особенность сжатия стоит держать в уме.
Измерение реального эффекта сжатия
Прежде чем считать настройку сжатия завершённой, стоит явно проверить, что заголовки ответа действительно отражают применённое сжатие, а не полагаться на то, что директивы конфигурации точно сработали как задумано.
curl -s -H "Accept-Encoding: gzip, br" -D - -o /dev/null https://example.ru/assets/app.css
В выведенных заголовках ответа стоит искать Content-Encoding — его наличие со значением gzip или br подтверждает, что сервер применил сжатие к этому конкретному ответу; отсутствие заголовка при ожидании сжатия обычно означает, что MIME-тип ответа не попал в gzip_types/brotli_types, либо размер ответа оказался меньше gzip_min_length, либо (для Brotli) модуль ngx_brotli не подключён в используемой сборке nginx.
Для сравнения размеров до и после сжатия удобно посмотреть заголовок Content-Length сжатого ответа и сопоставить его с размером того же файла без сжатия (запрос без заголовка Accept-Encoding или явно с Accept-Encoding: identity) — разница между этими двумя значениями и есть тот самый практический выигрыш от включённого сжатия конкретно для этого файла на конкретном сайте, а не усреднённая цифра из общих рекомендаций.
Инструменты разработчика в браузере (вкладка Network) показывают оба значения одновременно для каждого запроса — размер, переданный по сети (с учётом сжатия), и полный размер содержимого после распаковки, — что удобно для быстрой проверки эффекта сжатия сразу для всех ресурсов страницы без необходимости проверять каждый файл отдельно через curl.
Совмещение сжатия с кэшированием и CDN
Сжатие и кэширование ответов — взаимно дополняющие, а не конкурирующие оптимизации: закэшированный на стороне клиента или CDN ответ, который при этом ещё и сжат, комбинирует оба выигрыша — избегание повторной загрузки содержимого целиком и уменьшение объёма данных при первой (и при обновлении кэша) загрузке.
При использовании CDN или другого кэширующего прокси перед основным сервером важно, чтобы кэш хранил закэшированный ответ отдельно для каждого варианта Accept-Encoding, с которым он был запрошен, — именно для этого и служит заголовок Vary: Accept-Encoding, добавляемый директивой gzip_vary в nginx. Без него кэширующий узел рискует закэшировать один вариант ответа (скажем, сжатый Brotli) и затем ошибочно отдать его клиенту, вообще не заявляющему поддержку Brotli в своём запросе, что приведёт к ошибке декодирования на стороне такого клиента.
Многие современные CDN умеют сами применять сжатие к ответам исходного сервера на границе своей сети, даже если исходный сервер отдал несжатый ответ, — это может быть удобно как дополнительный уровень защиты на случай, если конфигурация основного сервера содержит ошибку, но полагаться исключительно на сжатие со стороны CDN не стоит: часть трафика между основным сервером и CDN всё равно останется несжатой, если сжатие не настроено на обоих уровнях.
Типичные ошибки настройки сжатия
Включение gzip или Brotli глобально без ограничения gzip_types/brotli_types конкретными текстовыми MIME-типами — в лучшем случае это бесполезно тратит CPU на попытки сжать уже сжатые бинарные форматы, в худшем — слегка увеличивает их размер за счёт служебных заголовков алгоритма сжатия.
Отсутствие заголовка Vary: Accept-Encoding при работающем сжатии за CDN или другим кэширующим прокси — риск отдать клиенту закэшированный ответ в формате сжатия, который этот конкретный клиент не поддерживает.
Использование максимального уровня сжатия (9 для gzip, 11 для Brotli) для сжатия на лету под реальной нагрузкой — прирост в размере ответа по сравнению со средним уровнем минимален, а нагрузка на CPU заметно выше, особенно на маленьком VPS с ограниченным числом ядер.
Отсутствие предварительного сжатия для статических файлов, которые не меняются между деплоями, — повторное сжатие одного и того же неизменного содержимого при каждом запросе тратит ресурсы сервера впустую там, где gzip_static и brotli_static решают задачу один раз при деплое.
Полное отсутствие модуля Brotli в используемой сборке nginx при уверенности, что он включён, — директива brotli on без реально собранного модуля ngx_brotli либо игнорируется, либо приводит к ошибке при проверке конфигурации, и стоит явно проверить вывод nginx -V перед тем, как полагаться на Brotli в продакшне.
Чек-лист настройки сжатия ответов
Шаг 1. Включите gzip с явным списком текстовых MIME-типов в gzip_types и разумным уровнем сжатия (5-6) для сжатия на лету.
Шаг 2. Проверьте наличие модуля Brotli через nginx -V и, если он доступен, включите brotli с тем же набором типов.
Шаг 3. Настройте предварительное сжатие статических файлов на этапе сборки или деплоя и включите gzip_static и brotli_static для их отдачи без повторного сжатия на лету.
Шаг 4. Убедитесь, что бинарные уже сжатые форматы (изображения, видео, архивы) не включены в списки типов для сжатия.
Шаг 5. Добавьте заголовок Vary: Accept-Encoding через gzip_vary, особенно при работе через CDN или другой кэширующий прокси.
Шаг 6. Проверьте реальный эффект через заголовки ответа (curl -I с Accept-Encoding) и вкладку Network инструментов разработчика, а не полагайтесь на предположения о том, что настройка сработала.
Итог: небольшая настройка с заметным эффектом
Сжатие HTTP-ответов — одна из немногих оптимизаций производительности сайта, которая внедряется исключительно на уровне конфигурации веб-сервера, без единой строчки изменений в коде приложения, и при этом даёт измеримое и почти всегда положительное влияние на скорость загрузки текстовых ресурсов страницы, особенно заметное на медленных мобильных соединениях.
Ключевое условие получить от неё реальную пользу, а не иллюзию оптимизации, — применять сжатие осмысленно: текстовые форматы через gzip и, где возможно, через более эффективный Brotli, бинарные уже сжатые форматы не трогать вовсе, а статику, не меняющуюся между запросами, сжимать заранее один раз вместо повторного сжатия на лету под нагрузкой.
Дальнейшее улучшение производительности отдачи статики обычно идёт рука об руку со сжатием — правильно настроенное кэширование на стороне клиента и CDN снижает саму частоту запросов к серверу, а сжатие уменьшает объём каждого из оставшихся запросов, и вместе эти две оптимизации дают заметно больший эффект, чем любая из них по отдельности.
Почему стоит выбрать нас
Быстрый запуск
Подберём конфигурацию и развернём проект в день обращения.
Надёжность
NVMe-диски, защита от DDoS и резервные копии по расписанию.
Поддержка 24/7
Дежурные инженеры на связи в любое время суток.
Настройка под ключ
Установим CMS, настроим веб-сервер, SSL и почту за вас.
Перенос без простоя
Аккуратно перенесём сайт и базу данных на новый сервер.
Прозрачные цены
Актуальные тарифы и честный подбор без навязывания.
Вопросы и ответы
Насколько сильно сжатие уменьшает размер ответа сервера?
Сильно зависит от типа контента: текстовые форматы вроде HTML, CSS, JavaScript и JSON сжимаются заметно, поскольку содержат много повторяющихся последовательностей символов (отступы, повторяющиеся имена тегов и свойств, служебные слова языка), тогда как уже сжатые форматы вроде JPEG, PNG, MP4 или ZIP почти не уменьшаются в размере при повторном сжатии, а иногда даже слегка увеличиваются из-за служебных заголовков алгоритма сжатия.
Чем Brotli лучше gzip, если оба делают в целом одно и то же?
Brotli использует более современный алгоритм сжатия со встроенным словарём типичных последовательностей, характерных для веб-контента (общие слова HTML, CSS-свойства, JavaScript-конструкции), что при сопоставимой скорости распаковки на стороне клиента обычно даёт заметно более компактный результат сжатия, чем gzip на том же уровне сжатия для того же файла.
Нужно ли включать одновременно и gzip, и Brotli?
Да, это стандартная практика: браузер сообщает серверу поддерживаемые алгоритмы через заголовок Accept-Encoding, и сервер должен уметь ответить тем форматом, который клиент поддерживает и предпочитает. Устаревшие клиенты и некоторые прокси не поддерживают Brotli, поэтому gzip остаётся обязательным запасным вариантом.
Что такое precompression и зачем сжимать файлы заранее?
Precompression — это заблаговременное сжатие статических файлов на этапе сборки проекта или деплоя, с сохранением результата рядом с оригиналом (например, style.css.gz и style.css.br), вместо сжатия на лету при каждом запросе. Это убирает нагрузку на CPU сервера в момент обработки запроса и позволяет использовать максимальный уровень сжатия, который слишком медленный для сжатия в реальном времени под нагрузкой.
Почему не стоит сжимать уже сжатые форматы вроде JPEG или MP4?
Эти форматы уже используют собственные алгоритмы сжатия с высокой энтропией результата — попытка сжать их ещё раз обычно почти не уменьшает размер (выигрыш на уровне долей процента), но добавляет ощутимую нагрузку на CPU сервера при каждой отдаче такого файла без пропорциональной пользы для конечного пользователя.
Как проверить, что сервер действительно отдаёт сжатый ответ?
Через заголовки ответа: наличие Content-Encoding: gzip или Content-Encoding: br в ответе сервера подтверждает, что сжатие сработало — это видно во вкладке Network инструментов разработчика браузера или через curl с флагом -I и явным указанием заголовка Accept-Encoding в запросе.
Какой уровень сжатия gzip оптимален для nginx?
Для сжатия на лету обычно рекомендуют средний уровень (gzip_comp_level 5 или 6 из максимальных девяти) — более высокие уровни дают лишь небольшой дополнительный выигрыш в размере, но заметно увеличивают нагрузку на CPU при каждом запросе; для предварительного сжатия статики на этапе сборки, где время сжатия не критично, можно использовать максимальный уровень.
Может ли сжатие ответов увеличить задержку ответа сервера?
Да, если сжатие выполняется на лету при каждом запросе, а не заранее: CPU сервера тратит время на сжатие перед отправкой ответа, и на маленьких, часто запрашиваемых файлах это время может оказаться заметным относительно самого времени передачи по сети, особенно при высоком уровне сжатия — поэтому небольшие статические файлы разумнее сжимать заранее один раз, а не на каждый запрос.
Как оплачивать услуги?
Оплата помесячная, для облачных серверов доступна почасовая тарификация. Принимаются карты, банковские переводы и электронные кошельки.
Есть ли круглосуточная поддержка?
Да, дежурные инженеры на связи 24/7.
Выдаёте ли закрывающие документы?
Да, предоставим необходимые документы для оплаты.
Как оставить заявку?
Заполните форму на странице или нажмите «Заказать» — мы свяжемся и поможем с подбором.