Nginx умеет сжимать ответы на лету и отдавать заранее подготовленные сжатые файлы. Gzip поддерживается из коробки, Brotli — через модуль
Модуль
Сжатие происходит в буфере, размер которого задаётся директивой
Разбор директив:
Brotli обеспечивает на 15–25% лучшее сжатие, чем gzip, для текстовых ресурсов. Поддерживается всеми современными браузерами. В Nginx Brotli доступен через сторонний модуль
На Ubuntu модуль доступен в пакете
После установки модуль подключается автоматически через файл в
Когда оба модуля активны, Nginx выбирает алгоритм по заголовку
Сжатие на лету расходует CPU. Для статики, которая редко меняется, выгоднее подготовить сжатые файлы заранее и отдавать их без повторного сжатия.
Флаг
Сжатие уменьшает объём передачи, но повторные запросы всё равно нагружают сервер. Заголовки кэширования говорят браузеру, что файл можно хранить локально.
Для файлов с версионированием в имени (
Ожидаемый вывод:
Должен присутствовать
Добавление
Без
Сжатие ответов размером 50–100 байт увеличивает их объём из-за gzip-заголовка (минимум ~20 байт). Устанавливайте порог не ниже 128–256 байт.
Если Nginx работает как reverse proxy, убедитесь, что
Добавьте переменную
Если сервер обрабатывает тысячи запросов в секунду, сжатие на лету может стать узким местом. Варианты:
Это выделяет 16 буферов по 8 КБ для сжатия. Если ответ не помещается, Nginx использует временные файлы.
После изменения конфигурации проверьте синтаксис и перезагрузите Nginx:
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_types | MIME-типы, к которым применяется 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
