Понеділок ранок. Lighthouse CI фейлиться на стейджинг-деплої. Оцінка продуктивності: 76. Минулого тижня: 96. Ніхто не пушив код на вихідних. Немає оновлень залежностей. Немає змін конфігу.
Я витратив 8 годин на пошук причини. Ось шлях зневадження.
Година 1: Очевидні перевірки
Перша думка: нестабільний тест. Lighthouse-оцінки варіюються на ±3–5 балів через мережеві умови та навантаження CPU. Падіння на 20 — не варіативність, а регресія.
Локальний запуск Lighthouse: 94. Достатньо близько до базового. Не регресія в коді.
Lighthouse на задеплоєному стейджинг-URL: 76. Збігається з CI.
Розрив між локальним і задеплоєним сказав мені: проблема в інфраструктурі, не в коді.
Година 2: Аналіз водоспаду
Chrome DevTools → вкладка Performance на стейджинг-URL. Водоспад показав:
- HTML: 180мс (нормально)
- Основний JS-бандл: 120мс (нормально)
- Файл шрифту: 2,100мс (ненормально — зазвичай 80мс)
- Hero-зображення: 350мс (нормально)
Файл шрифту вантажився з іншого CDN, ніж зазвичай. Google Fonts через fonts.googleapis.com. Нічого не змінювалось. Але час відповіді — змінився.
curl URL шрифту з Vercel edge: 2.3 секунди. З локальної машини: 45мс.
Година 3: Шрифтовий гак
Google Fonts був повільним з Vercel edge. Чому? CI-ранер був в іншому регіоні, і edge-функція робила крос-регіональний запит до Google Fonts. Заголовки відповіді: cache-control: private, max-age=86400. private означає, що CDN не може кешувати. Тільки браузер. На свіжому запуску Lighthouse (порожній кеш) кожен запит шрифту йде на origin.
Фікс: Селф-хостинг шрифтів. Завантажити WOFF2-файли, покласти в /public/fonts/, посилатися через @font-face в CSS.
@font-face {
font-family: 'Inter';
src: url('/fonts/inter-var.woff2') format('woff2');
font-display: swap;
font-weight: 100 900;
}
Після деплою з селф-хостингом: завантаження шрифту з 2,100мс до 40мс. Lighthouse: 88. Краще — але все ще не 96.
Година 5: Зображення, що не було lazy
Бракує 8 балів. Запустив Lighthouse з Treemap. LCP-елемент — hero-зображення, коректно. Але час LCP — 2.8с, було 1.2с.
Перевірив hero <Image>:
<Image src="/hero-bg.webp" alt="..." fill priority sizes="100vw" />
priority — коректно для hero. Тоді чому LCP повільний?
Вкладка Network. Hero-зображення вантажиться нормально. Але друге велике зображення нижче згину теж завантажувалось з priority:
// Секція About — нижче згину
<Image src="/about-photo.webp" alt="..." width={600} height={800} priority />
Хтось додав priority до фото секції About при оновленні контенту. Браузер попередньо завантажував два великих зображення одночасно. Hero-зображення конкурувало за bandwidth з about-фото, збільшуючи LCP на ~1.5 секунди.
Фікс: Прибрати priority зі зображення нижче згину.
Після деплою: LCP знизився до 1.3с. Lighthouse: 95.
Година 7: Чому CI зловив, а локально — ні
Локальний Lighthouse працює на повній пропускній здатності з низькою затримкою. Проблеми шрифту та зображення були приховані швидким локальним з'єднанням. CI-ранер симулює троттлінг мобільного з'єднання (4G), де конкуренція за bandwidth між двома priority-зображеннями та повільним шрифтом реально мала значення.
Саме тому ти запускаєш Lighthouse у CI, а не тільки локально.
Година 8: Запобігання в майбутньому
Три ґарди:
- Lighthouse CI budget: фейлить перевірку, якщо продуктивність падає нижче 90 або LCP перевищує 2.5с.
- Правило для
priorityна зображеннях: тільки одне зображення на сторінку має матиpriority. - Селф-хостинг шрифтів як правило проєкту: жодних посилань на Google Fonts.
Таймлайн
| Час | Дія | Оцінка |
|---|---|---|
| 0г | Помітив фейл CI | 76 |
| 2г | Визначив затримку Google Fonts з edge | 76 |
| 4г | Селф-хостинг шрифтів | 88 |
| 6г | Прибрав зайвий priority зі зображення нижче згину | 95 |
| 8г | Додав CI-бюджети + правила запобігання | 95 (захищено) |
Падіння на 20 балів. Дві кореневі причини, обидві невидимі локально. 8 годин на пошук, 10 хвилин на фікс. Ось так працює зневадження продуктивності.
Бачиш регресії продуктивності, які не можеш пояснити? Напиши — я тримаю Lighthouse 98 на продакшн-сайтах і можу проаудити твій пайплайн продуктивності.