Core Web Vitals: как измерить и улучшить
LCP, INP, CLS — пороги, инструменты измерения (PageSpeed Insights, CrUX, Lighthouse, web-vitals JS) и конкретные способы улучшить каждую метрику.
Содержание
Core Web Vitals — три метрики Google: LCP, INP и CLS. Они измеряют, насколько быстро и стабильно загружается страница для реального пользователя. С 2021 года это официальный ranking-фактор. В этой статье — что означает каждая метрика, где их измерить и конкретные действия для улучшения.
Полный технический контекст CWV в системе технического SEO — в руководстве Техническое SEO: с чего начать.
Три метрики: пороги и что они измеряют
| Метрика | Что измеряет | Зелёная зона | Жёлтая | Красная |
|---|---|---|---|---|
| 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 |
Google принимает решение на уровне URL: каждая страница оценивается отдельно. Для ranking-сигнала Google смотрит на 75-й перцентиль реальных пользователей за последние 28 дней (CrUX-данные).
INP заменил FID 12 марта 2024
До марта 2024 третьей метрикой CWV был FID (First Input Delay). FID измерял только первое взаимодействие. INP строже: фиксирует все клики, тапы и нажатия клавиш в течение сессии и берёт 98-й перцентиль. Если ваш сайт раньше имел хороший FID, но JS-код тяжёлый — INP может показать красную зону там, где FID был зелёным.
Где измерить Core Web Vitals
PageSpeed Insights — стартовая точка
Адрес: pagespeed.web.dev. Введите любой URL — получите два блока:
- Field data (реальные данные) — из CrUX за последние 28 дней. Это то, что Google использует для ranking. Если страница получает менее ~1000 визитов/месяц — CrUX-данных нет, появится сообщение «Insufficient data».
- Lab data (лабораторные данные) — Lighthouse в симулированных условиях (эмулированный Moto G Power, медленный 4G). Используйте для диагностики и локальной отладки.
Разница между field и lab может быть значительной: реальные пользователи имеют разные устройства, сети, кеш браузера. Цель — зелёная зона в field data, но если её нет — сначала исправьте lab data.
Google Search Console — масштаб по всему сайту
GSC → Core Web Vitals (боковая панель) показывает URL-группы со статусами Poor, Needs Improvement и Good для мобайла и десктопа отдельно. Здесь видно, какие шаблоны страниц имеют наибольшие проблемы: например, все страницы категорий в красной зоне, а главная — в зелёной.
GSC также предлагает кнопку «Validate Fix» после исправления — Google проверяет страницы из группы и обновляет статус в течение нескольких недель.
Chrome DevTools и Lighthouse
В Chrome: F12 → вкладка Lighthouse → запуск Performance аудита. Полезно для:
- Найти конкретный LCP-элемент.
- Увидеть «Opportunities» и «Diagnostics» — объяснение, почему метрика плохая.
- Сравнить до/после внесения правок локально.
Во вкладке Performance после записи загрузки: в треке Timings есть метки LCP, FCP с ссылкой на DOM-узел.
web-vitals.js — измерение в production
Библиотека от Google для сбора реальных CWV-данных с вашего сайта:
<script type="module">
import {onLCP, onINP, onCLS} from 'https://unpkg.com/web-vitals@4/dist/web-vitals.attribution.js';
function sendToAnalytics(metric) {
// Отправляем в GA4 как custom event
gtag('event', metric.name, {
value: Math.round(metric.name === 'CLS' ? metric.value * 1000 : metric.value),
metric_id: metric.id,
metric_value: metric.value,
metric_delta: metric.delta,
metric_rating: metric.rating, // 'good' / 'needs-improvement' / 'poor'
});
}
onLCP(sendToAnalytics);
onINP(sendToAnalytics);
onCLS(sendToAnalytics);
</script>
Пакет web-vitals также устанавливается через npm: npm install web-vitals. Attribution-версия добавляет детали: какой элемент вызвал LCP, какое взаимодействие дало худший INP, какие элементы сдвинулись при CLS.
CrUX API и дашборд
Chrome UX Report (CrUX) — публичный датасет Google. Доступ:
- CrUX API (бесплатно, 150 запросов в минуту):
https://chromeuxreport.googleapis.com/v1/records:queryRecord?key=API_KEY— данные по конкретному URL или домену. - CrUX дашборд: lookerstudio.google.com → ищите «CrUX Dashboard» — готовый Looker Studio отчёт, достаточно ввести домен.
- BigQuery: полный датасет, обновляется ежемесячно.
CrUX агрегирует данные за 28 дней скользящим окном и обновляется ежедневно для CrUX API (ежемесячно для BigQuery).
Lab vs Field: когда что использовать
| Ситуация | Какой источник |
|---|---|
| Проверить, будет ли сайт ранжироваться | Field data (CrUX, GSC) |
| Найти причину медленного LCP | Lab data (Lighthouse, PSI diagnostics) |
| Сравнить до/после правки локально | Lab data (DevTools, Lighthouse CI) |
| Мониторинг реальных пользователей | web-vitals.js → GA4 или другой коллектор |
| Масштаб проблем по сайту | GSC Core Web Vitals report |
LCP: как улучшить
LCP — время до отрисовки самого большого элемента above the fold. В 80%+ случаев это hero-изображение или крупный заголовок h1.
Шаг 1: найти LCP-элемент
PageSpeed Insights → раздел «Largest Contentful Paint element» покажет конкретный DOM-узел. Если это <img> — смотрите на оптимизацию изображений. Если <h1> — проблема в шрифтах или server response time.
Шаг 2: оптимизировать изображения
<!-- Плохо: нет размеров, нет WebP, нет preload -->
<img src="hero.jpg" alt="Hero">
<!-- Хорошо: WebP, srcset, правильные размеры, preload для LCP-изображения -->
<link rel="preload" as="image" href="hero-800w.webp"
imagesrcset="hero-400w.webp 400w, hero-800w.webp 800w"
imagesizes="(max-width: 640px) 400px, 800px">
<img src="hero-800w.webp"
srcset="hero-400w.webp 400w, hero-800w.webp 800w"
sizes="(max-width: 640px) 400px, 800px"
width="800" height="450"
alt="Hero"
fetchpriority="high">
Ключевые моменты:
- WebP или AVIF вместо JPEG/PNG. WebP: -25-35% от JPEG при той же качестве. AVIF: ещё -20% от WebP, но поддержка браузеров несколько меньше.
fetchpriority="high"для LCP-изображения — говорит браузеру загрузить первым.<link rel="preload">для LCP-изображения в<head>— браузер начинает загрузку до того, как парсит img-тег.loading="lazy"для изображений ниже первого экрана — не ставьте на LCP-элемент.
Шаг 3: уменьшить TTFB
Time to First Byte — время до первого байта ответа сервера. Цель: <800мс, идеал <200мс.
Способы:
- CDN (Cloudflare, Bunny.net, Fastly) — отдаёт HTML с edge-ноды рядом с пользователем.
- Кэширование на сервере — полноценный page cache для WordPress (LiteSpeed Cache, W3 Total Cache), статические генераторы (Hugo, Astro) дают TTFB <50мс по умолчанию.
- Оптимизация базы данных — долгие SQL-запросы напрямую увеличивают TTFB.
- Хостинг — shared hosting на переполненных серверах даёт TTFB 1-3с. VPS или managed WordPress хостинг — существенный скачок.
Шаг 4: Server-side rendering вместо client-side
Single-page applications (React, Vue, Angular) без SSR рендерят контент в браузере — LCP не может начаться, пока не загрузится и не выполнится весь JS-бандл. Решения:
- Next.js (React) с SSR или Static Site Generation.
- Nuxt.js (Vue) с SSR.
- Astro — отправляет ноль JS по умолчанию.
- Hugo — статический генератор, ультрабыстрый.
Шаг 5: Resource hints
<!-- DNS prefetch для внешних ресурсов (шрифты, CDN) -->
<link rel="dns-prefetch" href="https://fonts.googleapis.com">
<!-- Preconnect — устанавливает TCP/TLS до сервера заранее -->
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Preload — загружает ресурс с наивысшим приоритетом -->
<link rel="preload" href="/fonts/inter-v13-latin-regular.woff2" as="font" type="font/woff2" crossorigin>
INP: как улучшить
INP измеряет, насколько быстро страница реагирует на взаимодействие пользователя. Большинство проблем INP — это JavaScript, блокирующий main thread.
Найти проблемное взаимодействие
В web-vitals.js attribution-версия показывает, какое взаимодействие дало худший INP:
import {onINP} from 'web-vitals/attribution';
onINP(({value, attribution}) => {
console.log('INP:', value, 'ms');
console.log('Элемент:', attribution.interactionTarget);
console.log('Тип:', attribution.interactionType);
console.log('Время обработки:', attribution.processingDuration);
});
В Chrome DevTools → Performance → после клика на странице: найдите в треке Main длинные tasks (красные). Кликните на task — увидите, какая функция занимает время.
Уменьшить long tasks
Long task — любая задача на main thread дольше 50мс. INP становится плохим, если после клика есть long task, задерживающий ответ.
// Плохо: тяжёлый обработчик блокирует main thread
button.addEventListener('click', () => {
const result = heavyComputation(); // занимает 300мс
updateUI(result);
});
// Хорошо: разбиваем на chunks
button.addEventListener('click', async () => {
updateUI('Загружаю...');
// Передаём управление браузеру между итерациями
await scheduler.yield(); // Chrome 115+, или setTimeout(0) как fallback
const result = await computeInChunks();
updateUI(result);
});
Defer и async для скриптов
<!-- Блокирует парсинг HTML — избегайте для некритичных скриптов -->
<script src="analytics.js"></script>
<!-- async: загружает параллельно, выполняет как только загрузился -->
<script src="analytics.js" async></script>
<!-- defer: загружает параллельно, выполняет после парсинга HTML -->
<script src="non-critical.js" defer></script>
Google Tag Manager и большинство аналитических скриптов — используйте async. Скрипты, зависящие от DOM — defer.
Web Workers для тяжёлых вычислений
Если нужна тяжёлая обработка данных — выносите в Web Worker, чтобы не блокировать main thread:
// main.js
const worker = new Worker('heavy-worker.js');
button.addEventListener('click', () => {
worker.postMessage({data: largeDataset});
});
worker.onmessage = (e) => {
updateUI(e.data.result);
};
// heavy-worker.js
self.onmessage = (e) => {
const result = processData(e.data.data); // не блокирует UI
self.postMessage({result});
};
React: специфическая оптимизация
Если используете React — INP часто страдает из-за лишних re-renders:
// useMemo — кэшировать тяжёлые вычисления
const sortedItems = useMemo(
() => items.sort((a, b) => b.date - a.date),
[items]
);
// React.memo — не re-render если props не изменились
const ItemCard = React.memo(({item}) => <div>{item.title}</div>);
// useTransition — пометить обновление как несрочное
const [isPending, startTransition] = useTransition();
function handleFilter(value) {
startTransition(() => {
setFilteredItems(items.filter(i => i.name.includes(value)));
});
}
CLS: как улучшить
CLS измеряет, насколько страница «прыгает» во время загрузки. Каждый раз, когда элемент сдвигается без взаимодействия пользователя — это вносится в CLS.
Всегда задавать размеры для медиа
Самая распространённая причина CLS — изображения и видео без явных размеров. Браузер не знает размера, резервирует 0px, и после загрузки изображения весь контент ниже сдвигается.
<!-- Плохо: браузер не знает размеров -->
<img src="product.jpg" alt="Продукт">
<!-- Хорошо: браузер резервирует место заранее -->
<img src="product.jpg" width="600" height="400" alt="Продукт">
<!-- Для responsive изображений — aspect-ratio через CSS -->
<style>
.product-img {
aspect-ratio: 600 / 400;
width: 100%;
height: auto;
}
</style>
<img class="product-img" src="product.jpg" alt="Продукт">
То же самое для <video>:
<video width="1280" height="720" controls>
<source src="demo.mp4" type="video/mp4">
</video>
Резервировать место для рекламы и embeds
/* Баннер 728×90 — всегда резервируем место */
.ad-container {
min-height: 90px;
width: 728px;
}
/* Responsive вариант */
.ad-slot {
min-height: 250px; /* типичный размер medium rectangle */
}
Для динамического контента (чат, cookie banner, sticky notification):
- Cookie banner — показывайте снизу страницы, а не сверху. Banner сверху толкает весь контент вниз = CLS.
- Lazy-loaded контент — не вставляйте между существующими элементами. Добавляйте в конец или в заранее зарезервированное пространство.
Шрифты: font-display
Когда кастомный шрифт загружается позже системного — текст перерисовывается, что может вызвать CLS:
@font-face {
font-family: 'Inter';
font-display: optional; /* идеал для CLS: если не загрузился за ~100мс — не подменять */
src: url('/fonts/inter.woff2') format('woff2');
}
/* Или: swap — всегда показывать, подменять после загрузки */
/* Это может дать небольшой CLS, но лучше чем невидимый текст (FOIT) */
font-display: swap;
Preload важнейших шрифтов в <head> уменьшает задержку:
<link rel="preload" href="/fonts/inter-regular.woff2" as="font" type="font/woff2" crossorigin>
Типичные ошибки и как их избежать
- Измеряете только главную страницу. В GSC часто landing pages, статьи и страницы категорий имеют худшие CWV чем главная. Смотрите GSC Core Web Vitals report — он группирует страницы по шаблону.
- Ставите
fetchpriority="high"на все изображения. Если все «важные» — ни одно не важнее. Одинfetchpriority="high"на LCP-изображение. - Lazy load на LCP-изображение.
loading="lazy"на hero-изображении — самый быстрый способ испортить LCP. Lazy load только для изображений ниже первого экрана. - Большой JS-бандл без code splitting. Если весь frontend-код в одном файле на 500кб — браузер загружает и парсит всё перед рендерингом. Webpack/Vite code splitting + dynamic imports.
- Рекламный баннер без зарезервированного места. AdSense и programmatic реклама часто меняют размер после загрузки — CLS прыгает. Фиксируйте
min-heightконтейнера. - Игнорируете mobile данные. У большинства сайтов хуже мобильные CWV. Эмуляция в Lighthouse не передаёт реальный опыт — используйте field data.
- Не ждёте обновления CrUX. CrUX обновляется постепенно (28-дневное окно). Исправили CLS — GSC покажет улучшение через 4-6 недель, не завтра.
- GTM без оптимизации тегов. Google Tag Manager не блокирует LCP (асинхронный), но тяжёлые теги (рекламные SDK, heatmap, live chat) создают long tasks → плохой INP. Аудит активных тегов GTM — стандартная CWV-оптимизация.
Практическая очерёдность: что исправлять первым
Если все метрики красные — рекомендую такую очерёдность:
| Приоритет | Действие | Что фиксирует | Сложность |
|---|---|---|---|
| 1 | Добавить width/height к изображениям | CLS | Низкая — 1-2 ч |
| 2 | WebP/AVIF + fetchpriority="high" на LCP | LCP | Низкая |
| 3 | <link rel="preload"> для LCP-image и шрифтов | LCP | Низкая |
| 4 | CDN или page cache | LCP (TTFB) | Средняя |
| 5 | Cookie banner снизу, а не сверху | CLS | Низкая |
| 6 | defer/async для некритичных скриптов | INP (TBT) | Низкая |
| 7 | Аудит и очистка GTM-тегов | INP | Средняя |
| 8 | Code splitting JS-бандла | INP | Высокая |
| 9 | SSR/SSG вместо клиентского рендеринга | LCP + INP | Высокая |
Мониторинг после исправлений
Разового исправления недостаточно. CWV может ухудшиться после каждого релиза. Выстройте процесс:
- Lighthouse CI в CI/CD pipeline — автоматически проверяет CWV на каждом PR перед деплоем.
- web-vitals.js → GA4 — собирайте реальные данные постоянно, настройте алерты на ухудшение.
- GSC Core Web Vitals report — проверяйте раз в неделю после крупных релизов.
- PageSpeed Insights API — автоматические еженедельные снимки для ключевых страниц.
Пример еженедельного мониторинга через PSI API (bash):
URL="https://example.com"
API_KEY="YOUR_KEY"
curl -s "https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=${URL}&strategy=mobile&key=${API_KEY}" \
| jq '.loadingExperience.metrics | {
LCP: .LARGEST_CONTENTFUL_PAINT_MS.percentile,
INP: .INTERACTION_TO_NEXT_PAINT.percentile,
CLS: .CUMULATIVE_LAYOUT_SHIFT_SCORE.percentile
}'
Связанные ресурсы
Руководства:
- Техническое SEO: с чего начать — полный обзор технического SEO, crawling, indexing, hreflang, schema
Чек-листы:
- SEO Audit Checklist — пошаговый аудит включая CWV
Инструменты:
- Meta Tag Checker — проверить title, description, canonical одним запросом
- SERP Preview — предпросмотр сниппета
- Все инструменты сайта → /ru/tools/
Похожие статьи
Техническое SEO: полное руководство с чего начать — индексация, Core Web Vitals, schema.org, hreflang
Пошаговое руководство по техническому SEO для начинающих: crawling, indexing, robots.txt, sitemap.xml, canonical, hreflang, Core Web Vitals (LCP/INP/CLS), schema.org, mobile-first и типичные ошибки.
Seorobots.txt и sitemap.xml: полная настройка
Синтаксис robots.txt, что нельзя блокировать, директивы для AI-ботов, структура sitemap.xml, отправка в Google Search Console и типичные ошибки — практическая настройка.
ToolsRobots.txt Tester — проверка правил сканирования
Бесплатный онлайн-тестер robots.txt: проверяет разрешение на сканирование URL для 16 поисковых и AI-ботов (Googlebot, Bingbot, GPTBot, ClaudeBot), показывает правило и строку.