Три метрики, которые решают всё
Core Web Vitals — это три показателя, по которым Google оценивает, насколько комфортно реальному человеку пользоваться вашим сайтом. Не роботу, не лабораторному тесту, а живому посетителю с его мобильным интернетом и трёхлетним телефоном.
LCP (Largest Contentful Paint) — скорость загрузки основного контента. Проще говоря, сколько секунд человек смотрит на белый экран, прежде чем увидит что-то полезное. Хорошо — до 2.5 секунд. Всё, что дольше 4 секунд, — провал.
CLS (Cumulative Layout Shift) — визуальная стабильность. Это когда вы уже начали читать текст, а страница вдруг дёрнулась вниз из-за подгрузившегося баннера. Или когда вы целитесь пальцем в кнопку «Купить», а вместо неё нажимаете на рекламу, потому что макет сдвинулся. Хорошо — ниже 0.1.
INP (Interaction to Next Paint) — отзывчивость интерфейса. Заменил собой FID в марте 2024 года, и это принципиальное изменение. FID замерял только первый клик. INP отслеживает каждое взаимодействие на протяжении всей сессии — каждый тап, каждое нажатие клавиши — и фиксирует самое медленное. Хорошо — до 200 мс.
Почему это вообще важно? Потому что сайты, которые проходят все три порога, в среднем показывают на 24% ниже показатель отказов по сравнению с теми, кто не проходит. Это не мелочь, это прямое влияние на деньги.
Почему INP — самая коварная метрика прямо сейчас
Когда Google убрал FID и поставил на его место INP, многие даже не заметили. А зря. По данным Chrome UX Report на начало 2026 года, 43% сайтов не проходят порог в 200 мс по INP. Это самая провальная метрика из трёх.
Проблема в том, что FID был слишком мягким. Он замерял задержку только на первое действие — и только input delay, даже не полный цикл. Ваш сайт мог показать отличный FID, при этом на каждый последующий клик пользователь ждал по полсекунды.
INP так не обманешь. Он берёт все взаимодействия за сессию, выбирает наихудшее на 75-м перцентиле и именно его показывает как итоговый результат.
Вот история из моей практики. Один интернет-магазин на React — FID был идеальный, 12 мс. Клиент не мог понять, почему конверсия падает. Смотрим INP — 460 мс. Оказалось, при клике на фильтр товаров запускался синхронный пересчёт всего каталога, который блокировал основной поток на полсекунды. Пользователь нажимал на фильтр, и интерфейс «замирал». Решили через `requestIdleCallback` и разбивку задачи на чанки по 50 мс. INP упал до 140 мс. Конверсия через два месяца выросла на 11%.
Что конкретно делать с INP
Первым делом — найти проблемные взаимодействия. Chrome DevTools → Performance → записываете сессию с типичными действиями пользователя. Ищете Long Tasks (всё, что дольше 50 мс в основном потоке).
Дальше — по списку:
Разбивайте тяжёлые обработчики событий на мелкие задачи. Вместо одной функции на 300 мс — три по 80 мс с `setTimeout(0)` или `scheduler.yield()` между ними. Браузер успеет отрисовать кадр между задачами, и пользователь увидит отклик.
Убирайте из обработчиков кликов всё, что не влияет на визуальный ответ. Отправка аналитики, логирование, обновление глобального стейта — всё это можно делать после отрисовки.
Минимизируйте размер DOM. Каждый пересчёт стилей и layout дорожает с ростом количества узлов. На одном проекте у нас было 4800 DOM-элементов на странице каталога. Виртуализация списка через `react-window` сократила DOM до 200 элементов, INP упал вдвое.
Следите за third-party скриптами. Чат-виджеты, пиксели аналитики, A/B-тестирование — всё это работает в основном потоке и крадёт время у ваших обработчиков. Web Workers — ваш друг.
LCP: где обычно теряются секунды
LCP — метрика, которая кажется простой, но на деле зависит от целой цепочки событий. Прежде чем браузер покажет самый большой элемент во вьюпорте (обычно это hero-изображение или крупный текстовый блок), он должен:
- Получить первый байт ответа от сервера (TTFB)
- Распарсить HTML
- Загрузить критичные CSS и шрифты
- Скачать и отрендерить сам LCP-элемент
Каждый этап — это потенциальная потеря секунд.
TTFB — начните с сервера
В 2026 году ориентир — TTFB ниже 200 мс. Если ваш сервер отвечает за 800 мс, никакая оптимизация фронтенда вас не спасёт.
Самый быстрый способ уронить TTFB — CDN. Vercel Edge, Cloudflare Workers, AWS CloudFront — когда контент раздаётся с ближайшего к пользователю узла, задержка падает на 40–70%.
Для Next.js-проектов (а я работаю в основном с ним) серверные компоненты React — это подарок. Они рендерятся на сервере, и клиент получает готовый HTML. Никакого ожидания, пока скачается и выполнится бандл JS, прежде чем пользователь увидит контент.
Изображения — главный враг LCP
На подавляющем большинстве сайтов LCP-элемент — это картинка. И вот тут начинается самое интересное.
Конвертируйте в WebP или AVIF. На одном проекте простая перегонка hero-баннера из PNG в WebP сократила его вес с 1.2 МБ до 340 КБ. LCP улучшился на 1.4 секунды — только за счёт одного файла.
Добавьте preload для LCP-изображения. В `<head>` вашего документа:
<link rel="preload" fetchpriority="high" as="image" href="/img/hero.webp">Без этой строчки браузер узнаёт о существовании hero-картинки только когда дойдёт до неё в процессе парсинга HTML. С preload — начинает скачивать сразу.
`fetchpriority="high"` — относительно свежий атрибут, который явно говорит браузеру: «Эта картинка важнее остальных ресурсов, грузи её первой». На практике это даёт выигрыш в 200–600 мс.
Не ставьте `loading="lazy"` на LCP-элемент. Lazy loading — отличная штука для картинок ниже экрана. Но если вы повесите его на главную картинку, браузер специально отложит её загрузку. Я видел сайты, где LCP из-за этого составлял 6+ секунд.
Шрифты — тихий убийца LCP
Пользовательские шрифты загружаются асинхронно. Пока шрифт не скачался, браузер может показывать невидимый текст (FOIT) или системный шрифт-заглушку (FOUT). Если ваш LCP-элемент — текстовый блок, а шрифт грузится 2 секунды, то LCP = 2 секунды.
Решение: `font-display: swap` в `@font-face`. Браузер сразу покажет текст системным шрифтом, а когда кастомный подгрузится — подменит. В Next.js модуль `next/font` делает это автоматически и даже генерирует fallback-метрики, чтобы при подмене шрифта не было сдвига макета.
Ещё лучше — self-hosted шрифты вместо Google Fonts. Один DNS-запрос к fonts.googleapis.com + один к fonts.gstatic.com — это лишние 100–300 мс на мобильных. Скачайте шрифт, положите на свой сервер, подгружайте через preload.
CLS: почему страница «прыгает» и как это остановить
CLS — метрика, которую проще всего починить, но которую чаще всего игнорируют. А между тем, неожиданные сдвиги макета — это одна из самых раздражающих вещей для пользователя.
Причина №1: картинки и видео без размеров
Если вы не указали `width` и `height` для `<img>` или `<video>`, браузер при загрузке страницы не знает, сколько места нужно зарезервировать. Когда картинка наконец загрузится, всё содержимое ниже неё резко сдвинется вниз.
Решение элементарное — всегда указывайте размеры:
<img src="photo.webp" width="800" height="600" alt="Описание">Или через CSS `aspect-ratio`:
img {
aspect-ratio: 4 / 3;
width: 100%;
height: auto;
}Этот один приём закрывает примерно 60% всех проблем с CLS на среднестатистическом сайте.
Причина №2: динамически подгружаемый контент
Баннеры, рекламные блоки, куки-уведомления, встраиваемые виджеты — всё это появляется после загрузки основного контента и сдвигает его.
Под каждый такой элемент нужно заранее зарезервировать место. Для рекламного блока — фиксированный `min-height`. Для iframe — контейнер с заданным `aspect-ratio`. Для куки-баннера — фиксированное позиционирование (`position: fixed`), чтобы он не влиял на поток документа.
Причина №3: веб-шрифты
Я уже упоминал `font-display: swap` в контексте LCP. Но если метрики fallback-шрифта сильно отличаются от кастомного, при подмене текст поменяет размер и сдвинет всё вокруг. Это так называемый FOUT-shift, и он бьёт по CLS.
В Next.js это решается автоматически через `next/font` — он подбирает fallback с похожими метриками. Если вы не на Next — есть инструмент `fontaine` от Nuxt-команды, который делает то же самое.
Причина №4: внедрение элементов через JavaScript
Классика: скрипт загрузился и вставил баннер или уведомление в начало страницы. Весь контент уехал вниз.
Правило: любой JS, который модифицирует DOM выше текущей позиции скролла, должен делать это с предварительным резервированием пространства или через CSS-трансформации, которые не вызывают layout shift.
PageSpeed Insights: что означают цифры и каким верить
PageSpeed Insights показывает два типа данных, и их важно различать.
Полевые данные (Field Data) — это реальные метрики от реальных пользователей, собранные через Chrome UX Report за последние 28 дней. Именно эти данные Google использует для ранжирования. Если здесь всё зелёное — вы в порядке.
Лабораторные данные (Lab Data) — это синтетический тест через Lighthouse, запущенный с определённых условий (обычно мобильный эмулятор с троттлингом). Полезен для диагностики, но не влияет на ранжирование напрямую.
Я сталкивался с ситуацией, когда Lab-оценка была 45/100, а полевые данные — зелёные по всем трём метрикам. Клиент паниковал из-за цифры в Lighthouse, хотя для Google всё было отлично. И наоборот — Lighthouse показывал 92, а в поле CLS был красный, потому что на живом сайте подгружалась реклама, которой в лабораторном тесте нет.
Вывод: ориентируйтесь на полевые данные. Лабораторные — для отладки.
Инструменты, которые я использую ежедневно
Google Search Console → Core Web Vitals — общая картина по сайту, разбивка по мобильным/десктопным, группировка URL с похожими проблемами.
PageSpeed Insights — быстрый чек конкретной страницы, и полевые, и лабораторные данные в одном месте.
Chrome DevTools → Performance — детальный анализ. Записываете профиль, смотрите Long Tasks, Layout Shifts, находите конкретные элементы и скрипты-виновники.
Web Vitals Extension — расширение для Chrome, которое показывает LCP, CLS и INP в реальном времени прямо в браузере. Удобно при разработке — внесли изменение, обновили страницу, сразу видите результат.
WebPageTest — для глубокого анализа. Тестирование с разных локаций и устройств, filmstrip-просмотр загрузки кадр за кадром, waterfall-диаграмма со всеми запросами.
Что делать, если фреймворк «из коробки» уже помогает
Я работаю с Next.js, и это сильно упрощает жизнь. Компонент `<Image>` автоматически генерирует responsive размеры, конвертирует в WebP/AVIF, добавляет width/height для предотвращения CLS и лениво подгружает картинки ниже экрана.
Модуль `next/font` хостит шрифты локально, добавляет `font-display: swap` и генерирует fallback-метрики.
Server Components рендерят HTML на сервере, сокращая объём клиентского JS и улучшая как LCP, так и INP.
Но фреймворк — не волшебная палочка. Я видел Next.js-проекты с LCP 6 секунд, потому что разработчики тащили в клиентский бандл мегабайт зависимостей через `'use client'` или подключали пять аналитических скриптов без `strategy="lazyOnload"`.
Astro и Qwik идут ещё дальше. Qwik использует так называемую «resumability» — вместо гидратации всего приложения на клиенте, он «возобновляет» работу с того места, где закончил сервер. Это радикально снижает объём JS на клиенте и, как следствие, улучшает INP.
Как Core Web Vitals влияют на позиции — без мифов
Давайте честно: Core Web Vitals — не единственный фактор ранжирования и даже не главный. Контент, релевантность, ссылочная масса, авторитетность — всё это по-прежнему весит больше.
Но в конкурентных нишах, где у нескольких сайтов примерно одинаковый контент и похожий авторитет, Core Web Vitals работают как тай-брейкер. И данные это подтверждают: страницы на первой позиции в среднем на 10% чаще проходят все пороги CWV по сравнению с теми, кто на девятой позиции.
В Яндексе ситуация похожая, но с другим акцентом. Яндекс активно развивает метрику Проксима, которая оценивает полезность контента, и усиливает роль поведенческих факторов. Скорость загрузки напрямую влияет на поведенческие: если сайт медленный, пользователь уходит, время на сайте падает, показатель отказов растёт — и это сигнал алгоритму, что страница не удовлетворяет запрос.
Так что оптимизация Core Web Vitals — это не только про Google. Быстрый, стабильный, отзывчивый сайт выигрывает в любой поисковой системе, потому что он выигрывает у пользователя.
Чек-лист: что сделать прямо сейчас
Я не люблю статьи, которые грузят теорией, но не дают конкретного плана действий. Вот мой порядок работы, когда я берусь за оптимизацию нового проекта:
Шаг 1. Открываю Search Console → Core Web Vitals. Смотрю, какие группы URL в красной и жёлтой зонах. Определяю масштаб проблемы.
Шаг 2. Прогоняю типовые страницы (главная, каталог, карточка товара, статья) через PageSpeed Insights. Записываю полевые данные и список рекомендаций.
Шаг 3. Начинаю с LCP — потому что это обычно самый заметный для пользователя показатель. Проверяю TTFB, смотрю, есть ли preload для hero-изображения, конвертирую картинки в современные форматы, убираю render-blocking ресурсы из `<head>`.
Шаг 4. Фикшу CLS — прописываю размеры всем изображениям и iframe, резервирую место под динамический контент, проверяю шрифты.
Шаг 5. Берусь за INP — профилирую через DevTools, нахожу тяжёлые обработчики, разбиваю длинные задачи, выношу некритичную работу в Web Workers или откладываю через `requestIdleCallback`.
Шаг 6. Пересчитываю через неделю. Полевые данные обновляются с 28-дневным скользящим окном, поэтому мгновенного результата в Search Console не будет. Но тренд обычно виден через 2–3 недели.
Шаг 7. Встраиваю проверку в CI/CD. Lighthouse CI в пайплайне деплоя — если показатели просели ниже порогов, деплой не проходит. Это единственный способ не допустить регресса, когда над проектом работает команда.
Реальные результаты: три кейса
Интернет-магазин фотопечати. LCP до оптимизации: 4.2 с. После внедрения CDN, preload hero-изображения и конвертации в WebP — 1.9 с. CLS был 0.28 из-за динамических баннеров — зарезервировали место, поставили `font-display: swap` — стал 0.04. Показатель отказов на мобильных снизился с 62% до 41% за два месяца.
Информационный сайт с калькуляторами. INP был 520 мс — тяжёлые вычисления при каждом изменении поля ввода. Перенесли расчёты в Web Worker, добавили debounce в 150 мс — INP упал до 90 мс. Глубина просмотра выросла на 18%.
Лендинг портфолио. PageSpeed score был 38 на мобильных. Основная проблема — Hero-анимация на Canvas блокировала основной поток. Вынесли анимацию в OffscreenCanvas, отложили загрузку некритичных скриптов, оптимизировали шрифты — score вырос до 91. LCP с 5.1 с до 2.2 с.
Что я понял за эти полтора года
Производительность — это не разовая задача. Это дисциплина. Каждая новая фича, каждый новый скрипт, каждый A/B-тест может сломать то, что вы неделями выстраивали. Поэтому мониторинг и performance-бюджеты — не роскошь, а необходимость.
Не гонитесь за сотней в Lighthouse. Гонитесь за зелёными метриками в полевых данных. Идеальный синтетический тест не стоит ничего, если у реальных пользователей сайт тормозит.
И самое, пожалуй, важное: быстрый сайт — это не про SEO. Это про уважение к человеку, который пришёл на вашу страницу. Он не хочет ждать. Он не хочет, чтобы контент прыгал. Он хочет нажать на кнопку и увидеть результат. Если вы дадите ему это — он останется. А алгоритмы просто это зафиксируют.