Core Web Vitals: как измерить и улучшить

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 для шрифтов
  • Не вставляйте контент выше текущей области просмотра

Есть идея? Реализуем

Разрабатываем проекты, которые решают задачи бизнеса — от лендинга до сложного сервиса. Расскажите о своей задаче, подберём решение.

Написать в Telegram

29.03.2026

Нужна консультация?

Оставьте свои контактные данные, или свяжитесь с нами удобным для вас способом

Привет! Меня зовут Багира. Пишите, я все передам хозяевам!

Привет! Меня зовут Багира. Пишите, я все передам хозяевам!

Нажимая кнопку «Принять», вы соглашаетесь на сбор cookie. Мы используем их для обеспечения функционирования веб-сайта, аналитики действий и улучшения качества обслуживания. Если Вы не хотите, чтобы эти данные обрабатывались, отключите cookie в настройках браузера или прекратите использовать сайт.
Принять