16 серпня 2026 року наш власний сайт відповідав за 3,05 секунди. Сусідні проєкти на тому самому сервері відповідали за 0,2–0,3 секунди. Тобто хостинг був ні до чого: одна машина, одна мережа, різниця в десять разів.
Причину ми знайшли не в коді сторінки, а в блоці, який стоїть на кожній сторінці й на який ніхто не дивиться. Нижче розбір того випадку і ще трьох схожих, які ми виправили до 5 вересня, з цифрами до і після. Наприкінці покажемо, як за 10–15 хвилин перевірити те саме на своєму сайті.
Меню в шапці, яке важило 4,3 МБ
Ми заміряли не сторінку цілком, а кожне джерело даних окремо. Один запит займав 1 791 мс із 2,4 секунди всього рендеру. Це був запит для мега-меню в шапці, тобто він виконувався на будь-якій сторінці сайту: на головній, у статті, на сторінці послуги.
Меню потрібні три короткі поля на пункт: назва, заголовок і короткий опис. Запит брав 185 рядків разом зі стовпцем перекладів. А в тому стовпці лежали не лише назви, а й повний текст кожної сторінки трьома мовами: 23 КБ на рядок, 4,3 МБ на всі. Щоб прочитати кілька сотень байтів, база щоразу віддавала мегабайти.
Найкраща деталь: над цим запитом стояв коментар «легкий зріз — без важкого content JSON». Буквально коментар не брехав. Поле з текстом сторінки справді не вибиралось окремо, але воно лежало всередині поля перекладу, яке вибиралось цілком.
Чому цього ніхто не помітив
Бо в коді сторінки цього запиту немає. Відкриваєш шаблон статті, і там лише запити самої статті. Меню живе в шапці, шапка в загальному макеті, і коли шукають, чому повільна стаття, дивляться в статтю.
До того ж запит не був помилковим. Він повертав правильні дані, меню показувало правильні пункти. Повільний запит, який працює правильно, сам про себе не повідомляє.
Виправили двома кроками. Спершу потрібні поля стали добиратися окремим запитом за конкретними шляхами всередині JSON, а не цілим стовпцем. Сам цей запит прискорився з 1 891 до 107 мс. Потім готове дерево меню поклали в кеш даних на годину, зі скиданням, коли в адмінці публікують сторінку. Друге важливіше: з кешем цей запит на більшості показів не виконується взагалі.
Той самий візерунок ще тричі
Після меню ми почали шукати саме такі місця: невидимі зі сторінки й однакові для кожного показу. Знайшлось ще три.
Налаштування сайту, 23 серпня. Кожен рендер будь-якої публічної сторінки читав таблицю налаштувань 7–8 разів: теги аналітики (двічі, у двох різних місцях), рядки для структурованих даних, прапорець індексації, перелік увімкнених мов. Ці значення міняють з адмінки рідко, а читались вони на кожен запит. Того дня ми заміряли 0,46 с на простій сторінці й 0,72 с на складній, при тому що статичний файл із того самого сервера приходив за 0,30 с. Читання загорнули в кеш із терміном життя 60 секунд і скиданням при збереженні. Чому ще й хвилина, а не лише скидання: писати в ту таблицю вміє щонайменше дванадцять місць у коді, і пропустити одне дуже легко. Кеш без скидання гірший за відсутність кешу, бо правка з адмінки просто не з'являється.
Сторінки послуг, 5 вересня. Сторінки мережі послуг відповідали за 0,45–0,5 с, хаби розділів за 0,17 с, статті блогу за 0,36–0,43 с. Винен блок «суміжні послуги» внизу сторінки: він вибирав до 60 сусідів разом з повними перекладами. Виходило 1,1–1,3 МБ JSON проти 65 КБ усіх інших даних сторінки, хоча посиланням потрібні лише назва і заголовок однією мовою. Та сама помилка, що й у меню, тільки в іншому місці. Заодно з'ясувалось, що рядок самої сторінки читався двічі: для метаданих і для тіла.
Автоматичні посилання в статтях, 5 вересня. Тут винна вже не база, а процесор. Профіль показав, що найбільша одинична стаття витрат на сторінці статті — підстановка внутрішніх посилань. На кожен із приблизно 2 000 ключів збирався регулярний вираз і проганявся по всьому тексту. Це 35 мс процесора на показ, а на спільному сервері з load average 5–9 ці мілісекунди розтягувались у понад 0,1 с відповіді. Замінили на пошук підрядка з перевіркою меж слова. Стару й нову версію порівняли на 679 текстах (усі опубліковані статті й вступи сторінок трьома мовами): результат побайтово однаковий. Найважча стаття обробляється за 5,6 мс замість 18,9, увесь набір за 464 мс замість 1 451.
Чесне застереження: цифри «до» в цих випадках зняті в різні дні й різними способами. Це не одна крива, а чотири окремі історії, і складати їх в одне «прискорили в N разів» було б неправдою.
Скільки зараз і що з цього мережа
16 вересня 2026 року близько 20:40 за Києвом ми повторили замір. З самого сервера до публічної адреси сайту, по 5 запитів на адресу, медіана часу до першого байта:
| Тип сторінки | Медіана |
|---|---|
| сторінки послуг, 6 адрес трьома мовами | 0,211–0,266 с |
| статті, 3 адреси | 0,222–0,307 с |
| хаб розділу | 0,216 с |
| «Про нас» | 0,178 с |
| головна | 0,346 с |
Load average сервера в момент заміру був 6,1, тобто це цифри під звичайним для нього навантаженням, а не на порожній машині.
Тепер те, що бачить людина в Україні. З нашого ПК, по 6 запитів:
| Адреса | До першого байта |
|---|---|
| статичний файл логотипа | 0,185–0,215 с |
| «Про нас» | 0,254–0,362 с |
| стаття | 0,319–0,394 с |
| головна | 0,328–0,433 с |
Статичний файл сайт не рендерить узагалі, він просто лежить на диску. І все одно 0,19–0,22 секунди, з них близько 0,1 с лише на встановлення з'єднання. Це підлога: нижче неї оптимізація коду не опустить, бо це відстань і мережа, а не програма.
Як перевірити свій сайт за 10–15 хвилин
1. Зніміть підлогу. Візьміть будь-який статичний файл свого сайту (логотип, CSS) і сторінки, які вас турбують:
for u in /logo.svg / /katalog/; do
printf "%s ::" "$u"
for i in 1 2 3 4 5 6; do
curl -s -o /dev/null -w " %{time_starttransfer}" "https://ваш-сайт.com.ua$u"
done; echo
done
Від часу сторінки відніміть час статичного файлу. Залишок — це робота вашого сервера й вашого коду. Якщо залишок кілька десятків мілісекунд, у коді шукати нічого, питання в мережі або в розташуванні сервера. Якщо секунда, читайте далі.
2. Порівняйте різні типи сторінок. Головна, категорія, картка товару, стаття, «Контакти». Якщо повільні всі однаково, навіть найпростіші, причина майже напевно в тому, що є на кожній сторінці: шапка, меню, футер, налаштування. Якщо повільна лише категорія, дивіться її власні запити.
3. Знайдіть важкий запит. Увімкніть журнал повільних запитів і відкрийте сторінку кілька разів. Для MySQL:
SET GLOBAL slow_query_log = 1;
SET GLOBAL long_query_time = 0.1;
Для PostgreSQL з розширенням pg_stat_statements:
SELECT round(mean_exec_time) AS ms, calls, left(query, 120)
FROM pg_stat_statements ORDER BY mean_exec_time DESC LIMIT 10;
Запит із великим calls і помітним часом — головний підозрюваний: він виконується на кожен показ.
4. Подивіться, скільки важать стовпці, які він бере. У нашому випадку час ішов не на пошук, а на перекидання даних. У PostgreSQL:
SELECT avg(pg_column_size(назва_стовпця)) FROM назва_таблиці;
Якщо запит для меню бере стовпець на десятки кілобайтів на рядок, а показує з нього одне слово, ви знайшли те саме, що й ми.
Про OpenCart і подібні движки скажемо обережно, бо окремо ми це не міряли: там роль нашого мега-меню зазвичай грають модулі в шапці й футері, які виконуються на кожній сторінці. Перевіряти їх варто першими, але це аналогія, а не заміряний факт.
Що це не виправило
Найважливіше обмеження видно в даних реальних відвідувачів. За 28 днів до 7 вересня 2026 року 75-й перцентиль часу до першого байта на мобільних був 893 мс (109 замірів), на десктопах 654 мс (124 заміри). Рахуються лише відвідувачі, які погодились на аналітику, а вікно частково захоплює період до виправлень 5 вересня. Але розрив між 0,2 с на сервері й майже 0,9 с на телефоні — це переважно мобільна мережа, і кодом сайту його не прибрати. Допомогти могла б лише віддача готових сторінок із кешу, без рендеру. Українські сторінки в нас досі рендеряться на кожен запит: у їхніх адресах немає мовного префікса, шлях переписується, а переписаний запит іде повз кеш сторінок.
Кешування має й свою ціну. Меню оновлюється одразу після публікації в адмінці, а от налаштування сайту можуть відставати до хвилини. Ми на це свідомо погодились.
Висновок, який переносимо на будь-який сайт: перш ніж звинувачувати хостинг чи CMS, зніміть підлогу статичним файлом, а потім шукайте один важкий запит у блоці, який стоїть на кожній сторінці. Якщо займатися цим самим немає часу, ми робимо аудит швидкості сайту і прискорення з такими самими замірами до і після. Які числа швидкості взагалі міряти, розібрано окремо, а про вагу сторінки та стиснення є історія про 170 КБ замість 76.




