Обычная проверка сайта клининговой компании в PageSpeed Insights. Жду привычные цифры, а вижу: Largest Contentful Paint - Error! NO_LCP. Главная метрика скорости, по которой Google оценивает сайты, просто не измерялась. Не "плохая", не "красная" - её не было вообще. При этом остальное выглядело прилично: FCP 1,4 секунды, оценка 96 из 100. И до кучи пять аудитов диагностики падали со словом "Ошибка".
Прогнали ещё раз. И ещё. Мобильная версия, десктопная - везде NO_LCP.
Первая версия была удобной: это Google глючит. Проверили независимыми инструментами. Локальный Lighthouse той же версии 13.4.1, что у Google, - LCP измеряется, 1,9 секунды. SpeedVitals с сервером в Германии - Grade A, 94%, LCP 2,1. Полевые данные CrUX за 28 дней - LCP 1,7, Core Web Vitals пройдены. Три системы из четырёх меряют нормально. Значит, глючит движок PageSpeed, можно расходиться?
Не тут-то было. Прогнали через PSI второй сайт - личный, без сложной начинки. Всё измерилось идеально. Значит, что-то именно на этом сайте ломает замер.
Дальше - классический перебор подозреваемых. Бегущая лента из 22 логотипов партнёров: отложили старт на 2 секунды - NO_LCP на месте. Полупрозрачная hero-картинка с opacity 0.3 (новые Chrome исключают такие из кандидатов в LCP): сделали непрозрачной - локальный LCP стал красивым, в PSI по-прежнему NO_LCP. Коллтрекинг: скрипт подмены номеров сбрасывал кандидатов в LCP девять раз за загрузку - отключили, LCP упал с 6,7 до 3,9 секунды, но NO_LCP не делся никуда. Шрифты со свапом - мимо. Раздутый до 25 МБ трейс - похудел на треть, score локально 95, в PSI угадайте что.
Счёт к трём часам ночи: пять гипотез, пять частичных улучшений (сайт объективно стал быстрее!), ноль решений.
Ключевое решение ночи - перестать интерпретировать отчёты Lighthouse и напрямую спросить браузер. Взяли канареечный Chrome, запустили через puppeteer и повесили на страницу простейшую пробу: PerformanceObserver на largest-contentful-paint плюс слушатель скроллов. Контрольный сайт выдал две здоровые записи LCP. Наш - одну: крошечный логотип в шапке на 8 016 квадратных пикселей. Огромный заголовок и hero-картинку на треть экрана браузер будто не видел.
LCP-поток не сбоил - он умирал в первую секунду. А по спецификации наблюдение LCP необратимо останавливается ровно от двух вещей: действия пользователя или скролла.
Заблокировали вообще все скрипты на странице. Чистый HTML+CSS. И проба показала: scrolls: 4, первый на 1193 мс. Страница без единого скрипта скроллила сама себя.
Виновником оказалась карусель "Наши работы": в её CSS стояли scroll-snap-type: x mandatory и padding: 4px. Из-за паддинга первый слайд стоит в четырёх пикселях от края, mandatory-снап требует прижать его к снап-точке - и браузер в момент применения стилей сам доскролливает карусель на эти 4 пикселя. Никакого JS. А такое scroll-событие - программное, внутреннего контейнера, на 4 пикселя - навсегда останавливает наблюдение LCP в новых Chrome. Больше ни один элемент не может стать LCP. PageSpeed разводит руками: Error! NO_LCP.
Фикс - одна строка: scroll-padding-inline: 4px. Снап-точка учитывает паддинг, браузеру нечего доскролливать. Результат: LCP 0,9 секунды на десктопе, 2,9 на мобайле, CLS упал с 0,447 до 0,008 - в 50 раз, Performance 89-93, все аудиты живые.
Затраты: три часа расследования, восемь гипотез, полтора десятка замеров четырьмя инструментами. Решение - одна строка CSS против четырёх пикселей.
Могло ли это уронить SEO? Пока нет: Google ранжирует по полевым данным CrUX, а у реального человека первый же тап или скролл останавливает наблюдение LCP до того, как баг успевает сработать - позиции не пострадали. Но мина тикала: с мёртвым LCP невозможно заметить настоящую деградацию, реальные сдвиги макета уже копились, ужесточённая логика LCP из тестовых сборок Chrome со временем доезжает до стабильных - и до полевых метрик. А любой аудитор, открыв PageSpeed с пятью сломанными аудитами, сделал бы вывод "сайт сломан".
Главный практический вывод: зелёные полевые метрики - не повод игнорировать сломанную лабораторию. Полевые данные показывают прошлое - последние 28 дней существующей аудитории. Лаборатория - будущее: что увидит новый посетитель и новый браузер. Когда они противоречат друг другу, это не глюк одного из них - это разрыв, в котором прячется проблема.
Полная версия расследования - с кодом проб, трейсами и всеми уликами: dimaserbun.ru/blog/error-no-lcp-4-pikselya. Вопросы - в Telegram @dimik90.