Чому сайт швидкий локально, але повільний після публікації


На локальному комп’ютері сторінка відкривається майже миттєво. Після публікації — короткий білий екран, потім з’являється шапка, а головний банер підтягується ще за секунду. Зазвичай у цей момент починають стискати зображення, видаляти плагіни або міняти налаштування кешу. Іноді це допомагає, але частіше команда просто рухається навмання.

Причина в тому, що «сайт повільний» — не діагноз. Час може губитися до отримання HTML, під час роботи PHP і бази даних, у мережевому маршруті або вже в браузері, коли той розбирає CSS і виконує JavaScript. Тому перше завдання — не прискорити сторінку будь-якою ціною, а знайти конкретну ділянку затримки.

Чому локальна версія здається швидшою

На localhost майже немає мережевої відстані. Браузер не чекає віддалений DNS, TCP-з’єднання та TLS-узгодження, а PHP і база даних часто працюють на одному комп’ютері. Навіть неідеальний код у таких умовах може здаватися швидким.

Є й менш очевидна різниця. Локальна база зазвичай менша, кеш уже прогрітий, аналітика й чат вимкнені, а сайт перевіряє одна людина. На продакшені з’являються реальні дані, сторонні сервіси, паралельні запити та фонові завдання. Тому однаковий код ще не означає однакову швидкість.

Для чеснішого тесту відкрийте DevTools, перейдіть до Network, увімкніть Disable cache і виберіть мережеве обмеження на кшталт Fast 3G. Якщо локальна сторінка одразу перестала бути «миттєвою», частину її переваги створювали кеш і відсутність реальної мережі.

Що змінюється після публікації

Після перенесення запит проходить довший шлях:

DNS → вебсервер → застосунок → база даних → HTML → CSS, JavaScript, шрифти й зображення.

До цього додаються редиректи, HTTPS, обмеження тарифу, сторонні API та відмінності в конфігурації.

Один із типових кейсів — на локальному середовищі використовується production-збірка, а на сервер випадково потрапляє development-версія з великими initial chunks і відладочними скриптами. Інший — локально працює PHP 8.2 з OPcache, а на сервері застосунок запускається на старішій версії або з іншими модулями.

Перед оптимізацією складіть коротке порівняння середовищ:

  • версії PHP, Node.js і бази даних;
  • змінні .env;
  • режим налагодження;
  • активні модулі;
  • розмір бази;
  • наявність page cache.

Для базової перевірки достатньо кількох команд:

php -v
node -v
mysql --version

Окремо подивіться на редиректи. Ланцюжок HTTP → HTTPS → www → мовна версія додає кілька переходів ще до отримання корисного HTML.

У Network увімкніть Preserve log: якщо перед фінальним документом видно два-три запити зі статусами 301 або 302, це вже окрема точка для виправлення.

Як читати waterfall і не шукати проблему навмання

У waterfall спочатку відкривайте головний документ типу Doc, а не найбільше зображення. Якщо HTML очікується 1,3 секунди, а завантажується за 20 мс, стискання іконок не прибере початкову паузу.

У вкладці Timing важливі п’ять ділянок:

  • DNS Lookup — пошук IP-адреси домену;
  • Initial connection — встановлення з’єднання;
  • SSL — TLS-узгодження;
  • Waiting for server response — очікування першого байта;
  • Content Download — передавання самої відповіді.

Довгі DNS, Connect або SSL підказують мережеву проблему. Значний Waiting вимагає перевірити і сервер, і відстань до нього. Великий Content Download частіше означає надмірний розмір відповіді або повільний канал.

Після HTML дивіться, хто запускає наступні запити. Наприклад, головне зображення може бути задане як background-image у CSS. Браузер дізнається про нього лише після завантаження й розбору стилів, тому LCP починається пізніше, ніж для звичайного <img> у HTML. Колонка Initiator добре показує такі приховані залежності.

Ще один практичний прийом — Request blocking. Заблокуйте чат, карту або аналітичний скрипт і повторіть тест. Якщо FCP майже не змінився, але INP став кращим, сервіс не блокував перше відображення, зате перевантажував головний потік.

Як відрізнити TTFB, FCP, LCP, INP і CLS

Низький TTFB означає лише швидкий початок відповіді. Він не гарантує, що користувач швидко побачить головний блок або одразу зможе натиснути кнопку. Кожна метрика відповідає на своє питання.

Метрика Що бачить користувач Куди дивитися
TTFB Пауза до початку відповіді Мережа, PHP, база даних, кеш
FCP Момент появи першого видимого вмісту HTML, критичні стилі, шрифти
LCP Пізня поява головного блока Зображення, preload, пріоритет, CSS-залежності
INP Затримка після натискання Довгі JavaScript-завдання, обробники подій
CLS Стрибки макета Розміри медіа, банери, підміна шрифту

Умовний приклад: TTFB становить 1,2 секунди, а LCP — 3,9 секунди. Після вимкнення стороннього віджета LCP падає до 2,7 секунди, але TTFB не змінюється.

Отже, сторінка має дві різні проблеми:

  1. Повільну початкову відповідь.
  2. Важкий фронтенд.

Одним плагіном оптимізації це не закрити.

Lighthouse і DevTools показують конкретний лабораторний запуск. Польові дані відображають досвід реальних користувачів на різних пристроях і мережах. Якщо один тест швидкий, а польовий INP стабільно поганий, довіряти варто не найкращому запуску, а повторюваній картині.

Де фронтенд втрачає час

Файл може бути невеликим, але критичним. Наприклад, 40 КБ синхронного JavaScript у <head> здатні затримати розбір HTML сильніше, ніж велике зображення нижче першого екрана.

CSS і пізнє виявлення ресурсів

Великий CSS із тисячами невикористаних правил затримує побудову сторінки. Додаткові ланцюжки створюють @import, шрифти та фонові зображення.

Для головного зображення краще не покладатися на CSS, якщо воно є LCP-кандидатом. Звичайний елемент дає браузеру змогу побачити ресурс раніше:

<img
    src="/img/hero.avif"
    width="1280"
    height="720"
    fetchpriority="high"
    alt=""
>

fetchpriority="high" не слід ставити на всі зображення. Він доречний для одного справді важливого ресурсу першого екрана.

JavaScript і сторонні віджети

Скрипт, який не потрібен для початкового відображення, зазвичай варто завантажувати відкладено:

<script src="/js/app.js" defer></script>

defer зберігає порядок виконання й запускає скрипт після розбору HTML. async виконується одразу після завантаження, тому для залежних скриптів може створити помилки.

Але жоден атрибут не врятує, якщо після старту файл займає головний потік на 400–500 мс.

Показовий кейс — чат підтримки. Він може завантажуватися після сторінки й майже не впливати на FCP, але під час першої взаємодії виконати великий пакет JavaScript. Користувач уже бачить сайт, натискає меню й відчуває затримку. Це проблема INP, а не TTFB.

Що дають кешування та стиснення

Браузерний кеш, page cache, object cache і Brotli вирішують різні задачі:

  • браузер не завантажує незмінні файли повторно;
  • page cache не генерує той самий HTML на кожен запит;
  • object cache скорочує повторні звернення до даних;
  • Brotli зменшує обсяг текстових ресурсів у мережі.

Для статичних файлів із версіонованими назвами можна використовувати довгий кеш. У Nginx базове правило виглядає так:

location ~* \.(css|js|woff2|png|jpg|jpeg|webp|avif|svg)$ {
    expires 30d;
    add_header Cache-Control "public, max-age=2592000, immutable";
}

Важливо: таке правило не варто бездумно застосовувати до HTML або файлів, назва яких не змінюється після оновлення. Інакше браузер довго триматиме застарілу версію.

Перевірити заголовки можна командами:

curl -I https://example.com/assets/app.css
curl --compressed -I https://example.com/

У відповіді шукайте:

  • Cache-Control;
  • Content-Encoding: br;
  • Content-Encoding: gzip.

У DevTools порівняйте Size і Transferred. Якщо повторний запит береться з memory cache або disk cache, браузер не завантажував ресурс заново.

Тестуйте холодний і прогрітий кеш окремо. Швидке друге відкриття — добра новина, але новий користувач усе одно починає з першого.

Як перевірити сервер, а не просто звинуватити хостинг

Серверна гіпотеза стає переконливою, коли динамічна сторінка стабільно відповідає повільніше за статичний файл. Сам по собі довгий Waiting цього не доводить: до TTFB входить і мережевий шлях.

Створіть простий статичний HTML-файл і порівняйте його з типовою сторінкою застосунку. Якщо статика має TTFB 80 мс, а сторінка CMS — 1,1 секунди, мережа вже не виглядає головним підозрюваним.

Далі потрібно перевірити:

  • PHP;
  • SQL;
  • зовнішні API;
  • кеш.

Час окремих етапів можна виміряти так:

curl -o /dev/null -s -w "DNS: %{time_namelookup}\nConnect: %{time_connect}\nTLS: %{time_appconnect}\nTTFB: %{time_starttransfer}\nTotal: %{time_total}\n" https://example.com/

Повторіть запит кілька разів. Якщо перший запуск повільний, а наступні швидкі, можливо, прогрівається page cache або OPcache.

Якщо затримка виникає лише час від часу, зіставте її з:

  • резервним копіюванням;
  • антивірусним скануванням;
  • cron-завданнями;
  • піком трафіку.

PHP, база даних і зовнішні API

Для PHP корисний slowlog або APM. Для бази — журнал повільних запитів і план виконання SQL.

Сторінку іноді гальмує не один запит на дві секунди, а двісті коротких запитів по 8–10 мс. На WordPress це добре видно в Query Monitor, але на бойовому сайті такі інструменти слід використовувати обережно.

Окремий клас проблем — синхронний зовнішній API. Наприклад, сторінка товару перед рендерингом запитує курс валют у стороннього сервісу. Якщо той відповідає 900 мс, користувач чекає разом із PHP.

Тут допоможе кешування результату або винесення запиту з критичного шляху, а не стискання CSS.

Під час перевірки інфраструктури варто зіставити доступні ресурси, тип дискової підсистеми та розташування дата-центру; відповідні характеристики можна переглянути на сайті UkrLine.

Проте змінювати сервер має сенс лише після того, як вимірювання відокремили нестачу CPU, RAM чи дискової продуктивності від помилок коду та бази даних.

Коли проблема в мережі та чи допоможе CDN

Якщо сайт швидкий поруч із сервером, але повільний для віддалених користувачів, перевіряйте:

  • DNS;
  • Connect;
  • SSL;
  • маршрут;
  • втрати пакетів.

Фізична відстань має значення, але іноді більшу затримку створює невдалий обмін трафіком між провайдерами.

ping показує базову затримку, а traceroute, tracert або mtr допомагають побачити маршрут. Проте ICMP може блокуватися або оброблятися інакше, ніж HTTPS.

Тому остаточний висновок робіть за HTTP-таймінгами та тестами з регіонів, де перебуває аудиторія.

CDN корисний, коли значну частину затримки створює відстань або передавання кешованих:

  • CSS;
  • JavaScript;
  • шрифтів;
  • зображень.

CDN не виправляє:

  • повільний SQL;
  • важкий PHP-код;
  • чергу workers на origin-сервері.

Порівняйте cache hit, cache miss і прямий запит до origin. Якщо edge швидко віддає статику, але під час cache miss початкова пауза повертається, CDN прискорив лише частину ланцюжка.

Для персоналізованих сторінок додатково перевіряйте cookies і правила обходу кешу, щоб один користувач не отримав чужий HTML.

Практичний порядок діагностики

Найгірший сценарій — одночасно ввімкнути CDN, замінити плагін кешу, стиснути зображення й оновити PHP. Навіть якщо сайт стане швидшим, ви не знатимете чому.

Робочий підхід простіший:

Зафіксували базову лінію → висунули одну гіпотезу → змінили один параметр → повторили тест у тих самих умовах.

  1. Відтворіть проблему в інкогніто з вимкненим кешем.
  2. Відкрийте головний HTML і розділіть DNS, Connect, SSL, Waiting та Download.
  3. Перевірте редиректи.
  4. Зафіксуйте FCP та визначте LCP-кандидата.
  5. Знайдіть блокувальні стилі, синхронні скрипти й Long Tasks.
  6. Тимчасово заблокуйте сторонні віджети.
  7. Перевірте Cache-Control і Content-Encoding.
  8. Порівняйте статичну й динамічну відповіді.
  9. Перегляньте PHP slowlog, SQL і графіки ресурсів.
  10. Повторіть тест із регіону основної аудиторії.

У фіналі має з’явитися не фраза «сайт усе ще повільний», а конкретний висновок:

  • HTML генерується 1,2 секунди;
  • LCP-зображення виявляється тільки після CSS;
  • чат створює Long Task на 450 мс;
  • Connect і SSL помітно зростають за межами основного регіону.

Такий діагноз уже підказує наступну дію.

Швидкість localhost не доводить готовність сайту до продакшену. Надійний результат з’являється тоді, коли окремо виміряні сервер, мережа й браузер.

Оптимізувати потрібно не все підряд, а саме той етап, де вимірювання показало затримку.


2026-08-05

Зворотний зв'язок
Вхід