Core Web Vitals — метрики Google, которые напрямую влияют на позиции в поиске. Если сайт тормозит, прыгает при загрузке или долго реагирует на клики — позиции падают. Разбираемся, как измерить и исправить каждую метрику на практике.
Три метрики, которые решают всё
| Метрика | Что измеряет | Хорошо | Нужно улучшить | Плохо |
|---|---|---|---|---|
| LCP (Largest Contentful Paint) | Время загрузки основного контента | ≤ 2.5 с | 2.5 — 4 с | > 4 с |
| INP (Interaction to Next Paint) | Задержка реакции на действия пользователя | ≤ 200 мс | 200 — 500 мс | > 500 мс |
| CLS (Cumulative Layout Shift) | Визуальная стабильность — «прыжки» элементов | ≤ 0.1 | 0.1 — 0.25 | > 0.25 |
Как измерить Core Web Vitals
1. PageSpeed Insights
pagespeed.web.dev — показывает и лабораторные данные (Lighthouse), и полевые (CrUX — реальные пользователи Chrome). Полевые данные важнее — именно по ним Google оценивает сайт.
2. Google Search Console
Раздел Core Web Vitals показывает проблемы по страницам: какие URL проходят проверку, какие нет. Данные обновляются с задержкой в 28 дней.
3. Chrome DevTools
Вкладка Performance — запись загрузки страницы. Видно LCP-элемент, layout shifts, long tasks. Не забудьте включить CPU throttling 4x — тестировать на своём мощном компьютере бессмысленно.
4. Библиотека web-vitals.js
Собирайте реальные метрики с вашего сайта:
import { onLCP, onINP, onCLS } from 'web-vitals';
function sendToAnalytics(metric) {
const body = JSON.stringify({
name: metric.name,
value: metric.value,
rating: metric.rating, // 'good', 'needs-improvement', 'poor'
delta: metric.delta,
id: metric.id,
page: window.location.pathname,
});
// Beacon API — не блокирует закрытие страницы
if (navigator.sendBeacon) {
navigator.sendBeacon('/api/vitals', body);
} else {
fetch('/api/vitals', { body, method: 'POST', keepalive: true });
}
}
onLCP(sendToAnalytics);
onINP(sendToAnalytics);
onCLS(sendToAnalytics);
5. CrUX Dashboard
Бесплатный отчёт Google по реальным данным вашего сайта. Настраивается через Google Data Studio за 5 минут — показывает тренды метрик помесячно.
Оптимизация LCP: загрузка основного контента
LCP-элемент — обычно главная картинка, заголовок или видео. Определите его через DevTools → Performance → найдите маркер LCP.
Проблема: большие изображения
<!-- Плохо: огромный PNG без оптимизации -->
<img src="hero-banner.png" alt="Banner">
<!-- Хорошо: WebP, правильные размеры, приоритетная загрузка -->
<img
src="hero-banner.webp"
srcset="hero-banner-480.webp 480w,
hero-banner-800.webp 800w,
hero-banner-1200.webp 1200w"
sizes="(max-width: 768px) 100vw, 1200px"
alt="Banner"
width="1200"
height="630"
fetchpriority="high"
decoding="async"
>
Проблема: шрифты блокируют рендеринг
<!-- Предзагрузка критичного шрифта -->
<link rel="preload" href="/fonts/Inter-Regular.woff2"
as="font" type="font/woff2" crossorigin>
<!-- font-display: swap — показываем системный шрифт, пока грузится кастомный -->
<style>
@font-face {
font-family: 'Inter';
src: url('/fonts/Inter-Regular.woff2') format('woff2');
font-display: swap;
}
</style>
Проблема: медленный TTFB
Если сервер отвечает дольше 600 мс — никакая оптимизация фронтенда не спасёт.
- Включите серверное кэширование (Redis, Varnish)
- Используйте CDN (Cloudflare, BunnyCDN)
- Проверьте медленные SQL-запросы
- Для WordPress: объектный кэш + страничный кэш (WP Super Cache, LiteSpeed Cache)
Оптимизация INP: скорость реакции на взаимодействие
INP заменил FID в 2024 году и измеряет задержку всех взаимодействий за сессию — кликов, нажатий клавиш, тапов.
Проблема: тяжёлые обработчики событий
// Плохо: синхронная тяжёлая операция при клике
button.addEventListener('click', () => {
const data = heavyComputation(items); // блокирует main thread
renderResults(data);
});
// Хорошо: разбиваем на части через scheduler
button.addEventListener('click', async () => {
// Сначала — визуальный отклик
button.textContent = 'Загрузка...';
// Тяжёлую работу — в следующий кадр
await scheduler.yield();
const data = heavyComputation(items);
await scheduler.yield();
renderResults(data);
});
Проблема: слишком много JavaScript
<!-- Плохо: всё грузится сразу -->
<script src="analytics.js"></script>
<script src="chat-widget.js"></script>
<script src="carousel.js"></script>
<!-- Хорошо: отложенная загрузка некритичных скриптов -->
<script src="analytics.js" defer></script>
<script src="chat-widget.js" async></script>
Используйте import() для динамического импорта тяжёлых модулей:
// Модальное окно грузится только когда нужно
document.querySelector('.open-modal').addEventListener('click', async () => {
const { Modal } = await import('./components/Modal.js');
const modal = new Modal();
modal.open();
});
Оптимизация CLS: визуальная стабильность
CLS считает, насколько элементы «прыгают» во время загрузки. Каждый сдвиг — это потеря доверия пользователя.
Проблема: изображения без размеров
<!-- Плохо: браузер не знает размер до загрузки — контент прыгает -->
<img src="photo.jpg" alt="Photo">
<!-- Хорошо: размеры заданы — браузер резервирует место -->
<img src="photo.jpg" alt="Photo" width="800" height="600">
<!-- Для адаптивных изображений используйте aspect-ratio -->
<style>
.hero-image {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
}
</style>
Проблема: динамический контент без зарезервированного места
/* Баннер, который появляется и сдвигает контент */
/* Плохо */
.promo-banner {
display: none;
}
.promo-banner.visible {
display: block; /* всё ниже прыгает вниз */
}
/* Хорошо: резервируем место заранее */
.promo-banner {
min-height: 60px; /* место зарезервировано */
contain: layout;
}
.promo-banner:empty {
min-height: 60px;
}
Проблема: веб-шрифты меняют размер текста
/* Используйте size-adjust для минимизации сдвига */
@font-face {
font-family: 'Inter';
src: url('/fonts/Inter.woff2') format('woff2');
font-display: swap;
size-adjust: 107%; /* подобрать под ваш шрифт */
}
/* Или укажите fallback с похожими метриками */
body {
font-family: 'Inter', 'Segoe UI', system-ui, sans-serif;
}
Реальный кейс: интернет-магазин
| Метрика | До | После | Что сделали |
|---|---|---|---|
| LCP | 5.2 с | 1.8 с | WebP, preload главного баннера, CDN |
| INP | 380 мс | 120 мс | Отложили загрузку 3 виджетов, убрали синхронный JS |
| CLS | 0.32 | 0.04 | Размеры всех изображений, зарезервировали место под баннеры |
Результат: через 28 дней после исправлений позиции в Google выросли на 3 – 8 позиций по ключевым запросам.
Чек-лист оптимизации Core Web Vitals
LCP
- Изображения в формате WebP/AVIF с srcset
fetchpriority="high"на LCP-элементе- Preload критичных ресурсов (шрифты, главная картинка)
- Серверный кэш, CDN, TTFB < 600 мс
- Не блокируйте рендеринг CSS/JS в <head>
INP
- Уменьшите Total Blocking Time — разбейте long tasks
- Отложите загрузку некритичных скриптов (defer, async)
- Используйте динамический import() для тяжёлых модулей
- Избегайте layout thrashing (чтение-запись DOM в цикле)
CLS
- width и height (или aspect-ratio) на всех <img> и <video>
- Зарезервируйте место под рекламу и динамический контент
- font-display: swap + size-adjust для шрифтов
- Не вставляйте контент выше текущей области просмотра
Есть идея? Реализуем
Разрабатываем проекты, которые решают задачи бизнеса — от лендинга до сложного сервиса. Расскажите о своей задаче, подберём решение.

