Стиснення gzip nginx: де втрачається швидкість і трафік
Компресію ввімкнено, директива в конфігурації Nginx є, перезапуск виконано — а браузер усе ще отримує важку відповідь. Вага відповіді сторінки не змінюється, повільне перше завантаження залишається, і помітно це не в логах, а в метриках на боці клієнта.
Типова причина: стиснення gzip налаштовано, але спрацьовує не для всіх відповідей — частина йде повз правило з внутрішнього кешу, через проксі або як статика, для якої умова не збіглася.
Другий клас проблем — охоплення. Список типів вмісту за замовчуванням вузький: стилі, скрипти, векторна графіка, JSON до нього не потрапляють.
Одні відповіді стискаються на льоту під час кожного запиту, інші віддаються стисненими повторно — і клієнт розпаковує те, що вигідніше було отримати як є.
Перевіряється це лише за заголовками: чи дивиться сервер на формати, які підтримує клієнт, і що реально лежить у тілі відповіді.
Третє — вузьке місце віддачі. Стиснення виконується в момент надходження запиту, і ресурс витрачається на кожне звернення замість віддачі заздалегідь підготовленого файлу.
На піках це дає чергу: час відповіді зростає не через навантаження на канал, а через одну й ту саму повторювану роботу. Ціна вимірювана — у рази більше байтів на клієнта, просіла конверсія на повільному першому завантаженні й трафік, який зростає разом з аудиторією.
Нижче розбираємо, як це закривається: де саме втрачається стиснення, які правила це ловлять і як віддавати підготовлені архіви замість роботи на кожен запит.
Що дає правильне стиснення: час відгуку й обсяг
Обсяг передавання
Стискаються текстові ресурси: розмітка, стилі, скрипти, відповіді API у JSON. На типовому фронтенді це 60–80% ваги сторінки, і після налаштування вони йдуть у 3–5 разів меншим потоком.
Зображення, відео та шрифти вже стиснуті форматом — їх з політики виключають, щоб не витрачати процесор марно.
Час відгуку
Менше байтів дротом — швидший перший відрисований екран, особливо в мобільних мережах і віддалених регіонах. Brotli на статиці віддає помітно менший обсяг, ніж gzip на тому самому рівні: різниця в розмірі сягає 15–25% на стилях і скриптах. Для динаміки вмикають стиснення на льоту з помірним рівнем — виграш у часі переважує вартість.
Навантаження на процесор
Стиснення коштує циклів, тому його розводять за типами вмісту й рівнями. Статику готують заздалегідь і кешують, динаміку стискають на низькому рівні, дрібні відповіді не чіпають узагалі — на них заголовки важать більше за тіло. Так процесор зайнятий лише там, де це реально скорочує передавання.
Стійкість під навантаженням
Економія каналу — це запас на піках. Коли трафік зростає в рази, вузьке місце зміщується зі смуги на самі сервіси, і сайт тримає наплив без розширення каналу.
Віддача статики прискорюється ще й завдяки кешу на межі мережі: стиснуту відповідь віддають з нього, не доходячи до застосунку.
Що перевіряємо після налаштування
Вимірюємо частку стиснених відповідей, фактичний коефіцієнт за типами файлів і завантаження процесора в години пік. Правки вносимо за цифрами, а не «на око»: видно, які формати залишити в політиці, а які — виключити.
Де вмикати компресію: варіанти й обмеження
Точку вмикання вибирають за тим, хто володіє відповіддю і де доречно витрачати процесор. Компресія — ланка ланцюжка доставки: застосунок формує тіло, зворотний проксі маршрутизує запити, веб-сервер віддає статику, периметр — кеш.
Стискати у двох місцях підряд не можна: повторна обробка вже стиснутого тіла ламає відповідь, тому точка вибирається одна.
Критерії вибору: керування списком типів і мінімальною довжиною відповіді, наявність Vary: Accept-Encoding для коректної роботи кешів, ціна процесорного часу на запит і можливість зберігати готові архіви.
| Варіант | Коли підходить | Обмеження |
|---|---|---|
| Стиснення на веб-сервері | Змішаний трафік: статика й відповіді застосунку йдуть через один сервер, потрібні гнучкі правила за типами | Список типів і поріг довжини задаються вручну; помилка в правилах дає або нестиснуту відповідь, або зайве навантаження |
| Стиснення на зворотному проксі | Кілька сервісів за одним входом, потрібні єдині правила й кеш стиснених відповідей | Стискає за заголовками, вмісту не бачить; під час кешування варіанти треба розділяти за Accept-Encoding |
| Попередня підготовка файлів | Статика, яка змінюється рідко: збірка одразу кладе поруч .gz і .br |
Лише файли на диску; динаміку не покриває, архіви потрібно перезбирати під час кожного оновлення |
| Стиснення на периметрі доставки | Георозподілена аудиторія й великий обсяг статики | Налаштування залежать від майданчика; частина відповідей іде повз, якщо стиснення вже виконано на джерелі |
| Стиснення в застосунку | Відповіді формуються на льоту й не проходять через спільний сервер | Процесорний час витрачається на кожен запит, складніше кешувати й налагоджувати |
Порядок робіт: від аудиту віддачі до перевірки в проді
Стиснення — це не одна директива в конфігу, а ланцюжок рішень: що стискати, на якому вузлі ставити компресію і як не зламати вже стиснуті формати на кшталт зображень і відео. Нижче — порядок, за яким ми ведемо впровадження: ефект передбачуваний, а відкат займає хвилини.
-
Аудит віддачі. Знімаємо реальні розміри відповідей за типами вмісту: розмітка, стилі, скрипти, шрифти, відповіді API. Дивимося, що вже приходить стисненим від периметра. На виході — список типів, де стиснення дає виграш, і тих, де воно лише витрачає ресурс.
-
Схема обробки запиту. Фіксуємо, на якому вузлі застосовується компресія і що стискає периметр. Подвійне стиснення однієї відповіді — це навантаження без виграшу в розмірі.
-
Налаштування компресії. Вмикаємо gzip як базове покриття і Brotli для клієнтів, які його підтримують: на текстових типах він дає менший розмір.
Задаємо рівень стиснення, мінімальний розмір відповіді, список типів і винятки для вже стиснутих форматів; стиснутий і нестиснутий варіант мають коректно розділятися в кеші.
-
Перевірка перед викаткою. Порівнюємо розмір і час віддачі до й після на стенді, окремо на холодному й теплому кеші, проганяємо ключові сторінки й відповіді API.
-
План відкату. Конфігурація версіонується, зміни виїжджають однією правкою, яку знімають без перезбирання застосунку. Заздалегідь фіксуємо метрики, за якими вважаємо викатку невдалою.
-
Викатка й передача. Розкочуємо спочатку на частину трафіку, стежимо за часткою стиснених відповідей і часом віддачі, потім вмикаємо повністю. Передаємо команді конфіги, схему й коротку інструкцію з перевірки після релізу.
Конфігурації, схеми й регламенти на виході
Ми передаємо не лише працюючий сервер, а й набір артефактів, за якими стиснення далі ведеться власними силами. Кожен документ прив’язаний до конкретного місця в конфігурації, тому логіку правил не доводиться відновлювати з історії правок.
- Готові конфігурації сервера: рівні стиснення для статики й динаміки, поріг мінімального розміру відповіді, робота з попередньо стиснутими файлами — з поясненням, чому вибрано саме ці значення.
- Схема обробки запиту: шлях від клієнта до віддачі через проксі, кеш і застосунок, місце стиснення в цьому ланцюжку й точки, де відповідь може пройти повз нього.
- Політики винятків: перелік типів файлів, які стискати безглуздо — зображення, відео, архіви, уже упаковані формати.
- Регламент змін: як правки списків і рівнів проходять перевірку до викату, що фіксується в історії і за якою умовою відкочується.
- Документація з віддачі: які заголовки сервер зобов’язаний повернути, як переконатися, що відповідь прийшла стиснутою, куди дивитися в разі скарги на повільне завантаження.
- Набір перевірок і метрик: частка відповідей зі стисненням, обсяг передаваних даних до й після, час віддачі й частота помилок стиснення.
- Інструкція з підтримки: де змінювати винятки й рівні, як додати новий тип файлів і на що це вплине.
Кейс: прискорили віддачу каталогу без заміни серверів
Каталог великого постачальника: десятки тисяч карток, важкі шаблони, багато тексту й розмітки в кожній відповіді. Заміри показали, що вузьке місце не в сервері й не в базі — процесор і диск були завантажені наполовину, а сторінка гальмувала саме на передаванні.
Жодна текстова відповідь не йшла стиснутою: налаштування вимкнули ще під час первинного встановлення і з того часу до нього не поверталися.
Заміри до впровадження
Середня сторінка каталогу важила близько 1,2 МБ, з них на розмітку, стилі й скрипти припадало понад 80%. Час до першого відображення на середньому каналі — близько 3,4 с, частка стиснених відповідей — нуль.
Відмови на мобільних були помітно вищими, ніж на десктопі: різниця пояснювалася пропускною здатністю каналу, а не якістю верстки.
Що змінили і що вийшло
Увімкнули gzip для текстових типів і додали brotli з попереднім стисненням статики на диску — файли готувалися один раз, а не на кожен запит. Конфігурацію Nginx доповнили явним списком типів, відсіканням уже стиснутих форматів і кешуванням варіантів на проксі.
Частка стиснених відповідей зросла до 96%, вага сторінки каталогу впала до 210 КБ, час відгуку скоротився до 1,1 с — на тому самому залізі й без переписування серверної логіки.
Результат підтверджували за журналами доступу й замірами з різних регіонів, щоб виключити вплив кешу. Конфігурація лежить окремим файлом, тому повернення до попередньої поведінки — одна команда.
Чим відрізняється підготовка заздалегідь від обробки на льоту?
Різниця в тому, де і коли витрачається процесорний час. Під час підготовки заздалегідь стиснення виконується один раз — на етапі збірки або публікації вмісту, а результат лежить поруч з оригіналом окремим файлом.
Веб-сервер у цьому випадку лише вибирає відповідний варіант за заголовком Accept-Encoding і віддає готові байти, не навантажуючи процесор на кожен запит.
Обробка на льоту стискає тіло відповіді щоразу, коли його формує сервер.
Вартість процесорного часу зростає лінійно з трафіком: що вищий рівень стиснення, то вагоміший внесок у завантаження ядра, а на потоці дрібних відповідей накладні витрати переважують виграш в обсязі передавання.
Динамічний вміст — саме ця категорія: тіло залежить від запиту, сесії та даних, готовий файл для нього не зібрати.
Тут працюють два прийоми: кеш на периметрі, який тримає вже стиснуте подання й віддає його повторно без повторного стиснення, і відсікання за розміром і типами — відповіді менші за поріг стискати немає сенсу, вони лише витрачають процесор.
На практиці режими не конкурують, а ділять зони відповідальності: статика й шаблони — заздалегідь, динаміка — на льоту з помірним рівнем і ввімкненим кешуванням на периметрі. Такий розклад дає економію трафіку за передбачуваного навантаження на процесор.
Часті технічні питання щодо налаштування віддачі
Сумніви перед увімкненням стиснення зазвичай ті самі: як переконатися, що воно працює, чи не почне вміст стискатися двічі і що виключити з обробки. Короткі відповіді на кожен — нижче.
Як перевірити, що стиснення справді увімкнулося?
Запит із заголовком Accept-Encoding і відповідь із Content-Encoding: у тілі приходить той самий вміст, але з помітно меншим Content-Length.
Найпростіше порівняти розмір відповіді із заголовком і без нього, а за логами додатково подивитися коефіцієнт стиснення — так видно, що правило спрацювало не лише на розмітці, а й на стилях, скриптах і шрифтах.
Звідки береться подвійне стиснення?
Якщо перед сервером стоїть проксі або зовнішній кеш, який стискає відповіді сам, вміст іде закодованим двічі. Робочий варіант — залишити один рівень: або на краю, або на самому сервері.
Ми фіксуємо це до робіт, щоб потім не шукати причину зростання обсягу відповіді в частини користувачів.
Які типи файлів виключають?
За замовчуванням стискається лише розмітка, решту — стилі, скрипти, json, svg, шрифти — перелічують вручну. Бінарні формати й попередньо стиснуті копії віддають як є: виграшу в розмірі немає, а навантаження на процесор зростає на кожному запиті.
Що з кешуванням і заголовками?
Без заголовка Vary: Accept-Encoding проміжний кеш може віддати стиснутий варіант клієнту, який його не підтримує. Дату зміни зберігають, строк життя статики виставляють довгим — тоді повторні візити взагалі не зачіпають сервер.
Чи сумісно це з проксі та балансувальниками?
Так. Єдине, про що варто домовитися заздалегідь, — на якому рівні ввімкнено стиснення: якщо з’єднання термінується на балансувальнику, логічніше робити це там. Зазвичай так і з’ясовується, чому в частини аудиторії відповіді приходять без стиснення.
Обговоримо задачу: з чого починаємо і що потрібно від вас
Перша розмова потрібна, щоб побачити вашу схему обробки запитів цілком: де термінується захищене з’єднання, що стоїть перед застосунком, які відповіді вже йдуть стисненими, а які — як є.
За цією картиною стає зрозуміло, де увімкнення gzip і Brotli дасть ефект одразу, а де впреться в процесор або в проміжний кеш.
Далі потрібні вхідні дані — без них оцінка робіт перетворюється на здогадки. Зазвичай просимо:
- конфігурацію веб-сервера й зворотного проксі: де вмикається стиснення, які модулі зібрані;
- перелік типів вмісту й поточну частку стиснених відповідей: розмітка, JSON, стилі, шрифти, статика;
- схему кешування: проміжний проксі, мережа доставки вмісту, версіонування файлів;
- запас із процесора на вузлах, які стискають трафік, і профіль пікового навантаження;
- середовища й порядок випуску: як зміна доїжджає до продакшену і чи є тестовий контур;
- вимоги до відкату й вікна, коли конфігурацію можна перемикати без ризику;
- метрики, за якими ви судите про результат: час відповіді, обсяг трафіку, навантаження на процесор, частка попадань у кеш.
На виході ви отримуєте план робіт: що вмикаємо, у якому порядку, чим перевіряємо ефект і як відкочуємо за один крок. Опишіть задачу — повернемося з оцінкою й планом, а після погодження призначимо старт проєкту.







