Gzip и Brotli в Nginx: настройка сжатия ответов и кэширование статики

Nginx умеет сжимать ответы на лету и отдавать заранее подготовленные сжатые файлы. Gzip поддерживается из коробки, Brotli — через модуль ngx_brotli. Правильная настройка снижает объём передаваемых данных на 60–80% для текстовых ресурсов и ускоряет загрузку страниц.

Как работает gzip в Nginx​


Модуль ngx_http_gzip_module сжимает ответ перед отправкой клиенту, если браузер прислал заголовок Accept-Encoding: gzip. Nginx проверяет MIME-тип, размер ответа и уровень сжатия, затем формирует сжатый поток и добавляет заголовок Content-Encoding: gzip.

Сжатие происходит в буфере, размер которого задаётся директивой gzip_buffers. Если ответ больше буфера, Nginx пишет временные данные на диск — это замедляет обработку.

Базовая конфигурация gzip​


NGINX:
http {
    gzip on;
    gzip_vary on;
    gzip_min_length 256;
    gzip_comp_level 5;
    gzip_types
        text/plain
        text/css
        text/javascript
        application/javascript
        application/json
        application/xml
        application/xml+rss
        image/svg+xml;
}

Разбор директив:

ДирективаНазначение
gzip onВключает сжатие
gzip_vary onДобавляет Vary: Accept-Encoding, чтобы CDN и прокси кэшировали сжатые и несжатые версии отдельно
gzip_min_length 256Не сжимает ответы меньше 256 байт — сжатие мелких файлов увеличивает объём из-за накладных расходов
gzip_comp_level 5Уровень сжатия от 1 до 9. Уровень 5 даёт хороший баланс между скоростью и степенью сжатия
gzip_typesСписок MIME-типов для сжатия. text/html сжимается всегда, указывать его не нужно

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


  • Изображения (JPEG, PNG, WebP) — уже сжаты, повторное сжатие бесполезно и тратит CPU.
  • Видео и аудио — аналогично.
  • Файлы меньше 256 байт — накладные расходы gzip-заголовка превышают выигрыш.
  • Уже сжатые форматы: WOFF2, PDF с встроенным сжатием.

Brotli: модуль ngx_brotli​


Brotli обеспечивает на 15–25% лучшее сжатие, чем gzip, для текстовых ресурсов. Поддерживается всеми современными браузерами. В Nginx Brotli доступен через сторонний модуль ngx_brotli.

Установка на Ubuntu/Debian​


На Ubuntu модуль доступен в пакете libnginx-mod-brotli из PPA или репозитория Nginx:

Bash:
sudo apt install libnginx-mod-brotli

После установки модуль подключается автоматически через файл в /etc/nginx/modules-enabled/.

Конфигурация Brotli​


NGINX:
http {
    brotli on;
    brotli_comp_level 6;
    brotli_min_length 256;
    brotli_types
        text/plain
        text/css
        text/javascript
        application/javascript
        application/json
        application/xml
        image/svg+xml;
}

ДирективаНазначение
brotli onВключает сжатие Brotli
brotli_comp_level 6Уровень сжатия от 0 до 11. Уровень 6 — разумный баланс для продакшена
brotli_min_length 256Минимальный размер ответа для сжатия
brotli_typesMIME-типы, к которым применяется Brotli

Совместное использование gzip и Brotli​


Когда оба модуля активны, Nginx выбирает алгоритм по заголовку Accept-Encoding клиента. Если браузер поддерживает Brotli, будет использован он; иначе — gzip.

NGINX:
http {
    gzip on;
    gzip_comp_level 5;
    gzip_min_length 256;
    gzip_types text/plain text/css application/json application/javascript image/svg+xml;

    brotli on;
    brotli_comp_level 6;
    brotli_min_length 256;
    brotli_types text/plain text/css application/json application/javascript image/svg+xml;
}

Статическое предсжатие: gzip_static и brotli_static​


Сжатие на лету расходует CPU. Для статики, которая редко меняется, выгоднее подготовить сжатые файлы заранее и отдавать их без повторного сжатия.

Подготовка файлов​


Bash:
## Gzip
find /var/www/static -type f \( -name "*.css" -o -name "*.js" -o -name "*.html" -o -name "*.svg" \) \
    -exec gzip -k -9 {} \;

## Brotli
find /var/www/static -type f \( -name "*.css" -o -name "*.js" -o -name "*.html" -o -name "*.svg" \) \
    -exec brotli -k -q 11 {} \;

Флаг -k сохраняет оригинальный файл. Рядом с app.js появятся app.js.gz и app.js.br.

Конфигурация статического предсжатия​


NGINX:
server {
    listen 443 ssl;
    server_name example.com;
    root /var/www/static;

    gzip_static on;
    brotli_static on;

    location ~* \.(css|js|html|svg)$ {
        add_header Cache-Control "public, max-age=31536000, immutable";
    }
}

gzip_static on заставляет Nginx искать файл с расширением .gz и отдавать его, если клиент поддерживает gzip. brotli_static on делает то же самое для .br.

Кэширование статики на стороне клиента​


Сжатие уменьшает объём передачи, но повторные запросы всё равно нагружают сервер. Заголовки кэширования говорят браузеру, что файл можно хранить локально.

NGINX:
location ~* \.(css|js|woff2|png|jpg|webp|avif)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}

location ~* \.(html|json|xml)$ {
    expires 1h;
    add_header Cache-Control "public, must-revalidate";
}

Для файлов с версионированием в имени (app.3a7f2c.js) подходит immutable — браузер не будет проверять файл до истечения срока. Для HTML и API-ответов лучше короткий TTL с must-revalidate.

Проверка результата​


curl: проверка заголовков​


Bash:
## Gzip
curl -sI -H "Accept-Encoding: gzip" https://example.com/app.js | grep -i content-encoding

## Brotli
curl -sI -H "Accept-Encoding: br" https://example.com/app.js | grep -i content-encoding

Ожидаемый вывод: Content-Encoding: gzip или Content-Encoding: br.

Сравнение размеров​


Bash:
## Без сжатия
curl -so /dev/null -w "%{size_download}" https://example.com/app.js

## С gzip
curl -so /dev/null -w "%{size_download}" -H "Accept-Encoding: gzip" https://example.com/app.js

Проверка Vary​


Bash:
curl -sI https://example.com/app.js | grep -i vary

Должен присутствовать Vary: Accept-Encoding. Без него CDN может отдать сжатую версию клиенту, который не поддерживает сжатие, или наоборот.

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


Сжатие уже сжатых форматов​


Добавление image/jpeg, image/png, application/woff2 в gzip_types или brotli_types не даёт выигрыша, но нагружает CPU.

Отсутствие gzip_vary​


Без Vary: Accept-Encoding промежуточные кэши (CDN, корпоративные прокси) могут сохранить один вариант ответа и отдавать его всем клиентам, включая те, которые не поддерживают данный алгоритм сжатия.

Слишком высокий уровень сжатия​


gzip_comp_level 9 и brotli_comp_level 11 дают минимальный прирост по сравнению с уровнями 5–6, но увеличивают задержку и нагрузку на CPU. Для продакшена с высоким трафиком это критично.

gzip_min_length 0​


Сжатие ответов размером 50–100 байт увеличивает их объём из-за gzip-заголовка (минимум ~20 байт). Устанавливайте порог не ниже 128–256 байт.

Конфликт с proxy_pass​


Если Nginx работает как reverse proxy, убедитесь, что proxy_set_header Accept-Encoding передаётся на бэкенд, либо отключите сжатие на бэкенде и сжимайте только на Nginx. Двойное сжатие не происходит, но двойная проверка MIME-типов и размеров тратит ресурсы.

Мониторинг и тюнинг​


Логирование сжатия​


Добавьте переменную $gzip_ratio в формат лога, чтобы отслеживать степень сжатия:

NGINX:
log_format compression '$remote_addr - $request_uri - $gzip_ratio';

access_log /var/log/nginx/compression.log compression;

$gzip_ratio показывает отношение размера сжатого ответа к оригиналу в процентах. Значение 20% означает, что ответ сжался в 5 раз.

Нагрузка на CPU при высоком трафике​


Если сервер обрабатывает тысячи запросов в секунду, сжатие на лету может стать узким местом. Варианты:

  • Перейти на gzip_static / brotli_static для статики.
  • Снизить gzip_comp_level до 3–4.
  • Увеличить gzip_buffers для больших ответов.

NGINX:
gzip_buffers 16 8k;

Это выделяет 16 буферов по 8 КБ для сжатия. Если ответ не помещается, Nginx использует временные файлы.

Итоговая конфигурация для продакшена​


NGINX:
http {
    ## Gzip
    gzip on;
    gzip_vary on;
    gzip_min_length 256;
    gzip_comp_level 5;
    gzip_buffers 16 8k;
    gzip_types
        text/plain
        text/css
        text/javascript
        application/javascript
        application/json
        application/xml
        application/xml+rss
        image/svg+xml;

    ## Brotli
    brotli on;
    brotli_comp_level 6;
    brotli_min_length 256;
    brotli_types
        text/plain
        text/css
        text/javascript
        application/javascript
        application/json
        application/xml
        image/svg+xml;

    server {
        listen 443 ssl;
        server_name example.com;
        root /var/www/static;

        ## Статическое предсжатие
        gzip_static on;
        brotli_static on;

        ## Долгое кэширование для версионированной статики
        location ~* \.(css|js|woff2|png|jpg|webp|avif)$ {
            expires 1y;
            add_header Cache-Control "public, immutable";
        }

        ## Короткий TTL для HTML
        location ~* \.(html|json|xml)$ {
            expires 1h;
            add_header Cache-Control "public, must-revalidate";
        }
    }
}

После изменения конфигурации проверьте синтаксис и перезагрузите Nginx:

Bash:
sudo nginx -t && sudo systemctl reload nginx

Источники​


 
Назад
Верх Низ