Сжатие ответов — самый дешёвый способ ускорить сайт: HTML, CSS и JavaScript по сети занимают в несколько раз меньше места, а сервер тратит на это немного процессорного времени. В Nginx есть встроенный gzip и подключаемый brotli. Настроить их несложно, но реальная выгода зависит от типов файлов, уровня сжатия, кэширования и того, отдаёт ли сервер файлы сам или проксирует приложение. Ниже — рабочая конфигурация и проверка результата.

Как Nginx решает, сжимать ли ответ

Клиент присылает заголовок Accept-Encoding со списком поддерживаемых алгоритмов. Nginx смотрит на MIME-тип ответа, его размер и настройки модуля. Если тип не перечислен в gzip_types, ответ уйдёт без сжатия — даже при включённом gzip on. По умолчанию модуль сжимает только text/html; остальное нужно добавлять явно.

Сжатый ответ помечается заголовком Content-Encoding: gzip или br. При этом меняется тело, но не URL, поэтому кэши и прокси должны различать варианты по Vary: Accept-Encoding. Если заголовка нет, промежуточный кэш может отдать gzip-версию клиенту, который её не понимает.

Есть два режима: сжатие на лету для динамики и отдача заранее подготовленных файлов, которая экономит процессор и позволяет использовать максимальный уровень brotli.

Базовая настройка gzip

Проверенную конфигурацию удобно держать в отдельном файле и подключать из блока http или конкретного server:

gzip on;
gzip_vary on;
gzip_comp_level 6;
gzip_min_length 1024;
gzip_proxied any;
gzip_types text/plain text/css text/xml application/json
           application/javascript application/xml image/svg+xml;
gzip_static on;

gzip_comp_level 6 — разумный компромисс: уровень 9 даёт единицы процентов выигрыша, но заметно нагружает процессор на больших файлах. gzip_min_length 1024 отсекает мелкие ответы, где служебные заголовки съедают всю экономию. gzip_proxied any нужен, если перед Nginx стоит прокси или балансировщик и запросы приходят с заголовком Via. gzip_static on включает отдачу готовых файлов .gz, если они лежат рядом с оригиналом; соответствующий модуль должен быть собран в бинарнике.

После правки проверяем синтаксис и перезагружаем сервер без разрыва соединений:

nginx -t
systemctl reload nginx
curl -sI -H 'Accept-Encoding: gzip' https://example.com/ | grep -i content-encoding

Brotli: модуль, которого нет в официальной сборке

Brotli обычно сжимает текстовые ответы на 10–20% сильнее gzip при сопоставимом времени, а на статических файлах с уровнем 11 разница бывает ещё заметнее. Но ngx_brotli не входит в официальные сборки Nginx. Сначала смотрим, что уже есть в системе:

nginx -V 2>&1 | tr ' ' '\n' | grep -i brotli
ls /usr/lib/nginx/modules/ | grep -i brotli

Если модуль поставляется пакетом дистрибутива, достаточно установить пакет и подключить модуль; путь к .so уточните в файлах дистрибутива. Если пакета нет, модуль собирают под ту же версию Nginx с флагом --with-compat; неподходящий бинарный модуль приведёт к ошибке загрузки. Типовая настройка выглядит так:

load_module modules/ngx_http_brotli_filter_module.so;
load_module modules/ngx_http_brotli_static_module.so;

brotli on;
brotli_comp_level 5;
brotli_min_length 1024;
brotli_types text/plain text/css application/json application/javascript
             application/xml image/svg+xml;
brotli_static on;

Уровень 5 для динамики — безопасный старт. Уровни 9–11 имеют смысл только для предварительно сжатых файлов: на каждый запрос такой компрессор тратит слишком много процессорного времени. Nginx выбирает brotli, если клиент прислал br в Accept-Encoding, иначе отдаёт gzip или несжатый ответ.

Предварительное сжатие статики

Самый предсказуемый вариант для CSS, JS и SVG — заранее создать .gz и .br рядом с исходниками и включить gzip_static и brotli_static. Nginx не будет сжимать ничего на лету, а отдаст готовый файл. Сборку удобно делать в CI или в скрипте деплоя:

find public/assets -type f \( -name '*.css' -o -name '*.js' -o -name '*.svg' \) -exec gzip -9 -k -f {} \;
find public/assets -type f \( -name '*.css' -o -name '*.js' -o -name '*.svg' \) -exec brotli -q 11 -k -f {} \;
ls -lh public/assets/app.css*

При обновлении файла пересобирайте оба архива. Для сайта на CDN убедитесь, что он не сжимает ответ повторно и умеет отдавать .br с корректным Content-Type.

Что сжимать не нужно

  • JPEG, PNG, WebP, AVIF и GIF — они уже сжаты, повторная обработка только тратит ресурсы.
  • Видео, аудио, ZIP, PDF и другие контейнеры с собственным сжатием.
  • Шрифты WOFF2: формат уже использует сжатие, выигрыш близок к нулю.
  • Очень маленькие ответы — меньше 1 КБ, если только это не критичный JSON.

Текстовые форматы — HTML, CSS, JS, JSON, XML, SVG и plain text — сжимаются хорошо, часто на 60–80%.

Как измерить реальную выгоду

Заголовки показывают только факт сжатия, но не размер и время. Сравним три варианта одним и тем же запросом:

for enc in identity gzip br; do
  curl -s -o /dev/null -w "$enc: %{size_download} bytes, %{time_total} s\n" \
    -H "Accept-Encoding: $enc" https://example.com/assets/app.js
done

size_download — это переданные байты без распаковки, поэтому цифры можно сравнивать напрямую. Для честной оценки повторите замер несколько раз и учитывайте кэш: второй запрос может прийти из CDN и не отражать работу Nginx. Полезно также посмотреть Content-Length и наличие Vary:

curl -sI -H 'Accept-Encoding: gzip, br' https://example.com/assets/app.js \
  | grep -iE 'content-encoding|content-length|vary|cache-control'

Типичные ошибки

  • Нет Vary. Кэш отдаёт сжатый вариант клиенту без поддержки gzip. Лечится gzip_vary on и аналогичной настройкой на CDN.
  • Двойное сжатие. Nginx сжимает ответ, а CDN или приложение — ещё раз. Проверяйте цепочку заголовков и отключайте лишний слой.
  • Уровень 9 на динамике. При росте нагрузки время ответа растёт быстрее, чем падает трафик. Для динамических страниц достаточно 5–6.
  • Сжатие уже сжатых файлов. Процессор тратится, размер почти не меняется.
  • Забытый gzip_proxied. За балансировщиком ответы внезапно перестают сжиматься.
  • Отсутствие проверки после деплоя. Обновлённый конфиг может не примениться из-за ошибки в другом файле; nginx -t обязателен.

Итог

Для большинства сайтов достаточно gzip с уровнем 5–6 и предварительного сжатия статики. Brotli даёт заметную экономию трафика, но требует отдельного модуля и аккуратной сборки архивов; на динамике не стоит выставлять максимальный уровень. Проверяйте не факт заголовка, а размер, время и поведение кэша. Тогда сжатие остаётся дешёвой оптимизацией, а не источником загадочных ошибок.

Минимальный чек-лист: nginx -t проходит, Vary присутствует, статика отдаётся как .br или .gz, уровни сжатия разумны, а после деплоя вы повторили замеры через curl.

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка 0 / 5. Количество оценок: 0

Оценок пока нет. Поставьте оценку первым.

Поделиться: Telegram ВКонтакте