Клавиша / esc

LCP (Largest Contentful Paint)

Что такое метрика Largest Contentful Paint в Core Web Vitals

Время чтения: 12 мин

Что такое LCP

Скопировано

LCP (Largest Contentful Paint) измеряет время от начала загрузки страницы до момента, когда в области просмотра (viewport) был отрисован самый большой элемент контента. Обычно это главное изображение (hero image), обложка статьи, большой текстовый блок или постер видео. LCP-элемент нельзя назначить вручную — браузер сам определяет его во время загрузки страницы, а проверить, какой именно элемент он выбрал, можно в PageSpeed Insights или на вкладке Performance в DevTools.

Хорошим считается LCP до 2,5 секунды, плохим — больше 4 секунд.

Пока главный контент не появился, пользователь не понимает, туда ли он попал и может ли взаимодействовать со страницей. Плохой LCP повышает риск раннего ухода со страницы и сокращает число людей, которые доходят до целевого действия.

Пример сценария: пользователь открывает страницу товара, но его крупное изображение появляется только через пять секунд. Не увидев основной контент, человек может решить, что страница работает слишком медленно, и вернуться к результатам поиска.

Из чего складывается LCP

Скопировано

Чтобы улучшить LCP, сначала нужно понять, на каком этапе теряется время. Google выделяет четыре основных этапа и ориентировочную долю времени, которую каждый из них занимает у хорошо оптимизированной страницы — на практике соотношение сильно зависит от конкретного сайта:

Google разбивает LCP на четыре основных этапа: TTFB, задержка загрузки ресурса, длительность загрузки и задержка отрисовки
  1. TTFB (ориентировочно ~40% времени) — время от начала перехода на страницу до получения первого байта ответа от сервера, подробнее — в статье про TTFB. Пока браузер не получил первый байт ответа, он не может начать парсить HTML и находить ресурсы, нужные для отображения LCP-элемента;
  2. задержка загрузки ресурса (обычно до 10%) — время между получением первого байта HTML и началом загрузки ресурса, нужного для отображения LCP-элемента. Например, браузер может поздно обнаружить главное изображение или другой ресурс LCP-элемента;
  3. длительность загрузки ресурса (ориентировочно ~40%) — время, которое требуется для загрузки ресурса LCP-элемента. На этот этап влияют размер файла, скорость сети, формат ресурса и способ загрузки;
  4. задержка отрисовки (обычно до 10%) — время между моментом, когда ресурс уже доступен, и фактическим появлением элемента на экране. На этом этапе задержку чаще всего создают блокирующий CSS и JavaScript, длинные задачи (Long Tasks), ожидание загрузки шрифтов и другие операции, откладывающие отрисовку.

Если LCP-элемент не зависит от загрузки отдельного ресурса (например, это крупный текстовый блок), то этапы «задержка загрузки ресурса» и «длительность загрузки ресурса» отсутствуют. В этом случае итоговое значение LCP складывается только из TTFB и задержки отрисовки.

Как улучшить LCP

Скопировано

TTFB

Скопировано

Более подробно в статье о TTFB. Тут кратко:

  • уберите лишние редиректы в цепочке до страницы;
  • оптимизируйте свой веб-сервер;
  • пользуйтесь CDN для статических файлов и не забудьте настроить на нём кеш, чтобы контент отдавался с ближайшего к пользователю узла.

Задержка загрузки ресурса

Скопировано

Чтобы браузер узнал про LCP-картинку как можно раньше:

  • держите LCP-картинку в HTML обычным <img src>, не подставляйте её с помощью JavaScript и не прячьте в data-src. Тогда её найдёт предварительный сканер разметки (preload-сканер). Он вычитывает HTML и начинает качать ресурсы ещё до выполнения скриптов;
  • не используйте loading="lazy" на LCP-картинке: ленивая загрузка откладывает именно то, что должно появиться первым;
  • поставьте fetchpriority="high" на главную картинку — браузер начнёт загружать её с более высоким приоритетом. Не назначайте высокий приоритет более чем 1–2 ресурсам, иначе теряется смысл в оптимизации;
  • в Next.js укажите priority у LCP-картинки в компоненте <Image>: это отключит ленивую загрузку и выставит запросу высокий приоритет:
        
          
          import Image from "next/image";import hero from "./hero.png";<Image src={hero} alt="Обложка" priority placeholder="blur" />;
          import Image from "next/image";
import hero from "./hero.png";

<Image src={hero} alt="Обложка" priority placeholder="blur" />;

        
        
          
        
      

Если LCP-изображение задаётся через CSS background-image, браузер обнаружит его только после загрузки и разбора CSS. По возможности используйте <img>, а если это невозможно, добавьте <link rel="preload" as="image" fetchpriority="high">:

        
          
          <!-- LCP-картинка: в HTML, с высоким приоритетом, без lazy --><img  src="hero.avif"  alt="Обложка"  width="1200"  height="630"  fetchpriority="high"/><!-- если картинка ссылается только из CSS — предзагрузим её --><link rel="preload" as="image" href="hero.avif" fetchpriority="high" /><!-- для адаптивной предзагрузки — imagesrcset/imagesizes --><link  rel="preload"  as="image"  imagesrcset="hero-800.avif 800w, hero-1600.avif 1600w"  imagesizes="100vw"  fetchpriority="high"/>
          <!-- LCP-картинка: в HTML, с высоким приоритетом, без lazy -->
<img
  src="hero.avif"
  alt="Обложка"
  width="1200"
  height="630"
  fetchpriority="high"
/>

<!-- если картинка ссылается только из CSS — предзагрузим её -->
<link rel="preload" as="image" href="hero.avif" fetchpriority="high" />

<!-- для адаптивной предзагрузки — imagesrcset/imagesizes -->
<link
  rel="preload"
  as="image"
  imagesrcset="hero-800.avif 800w, hero-1600.avif 1600w"
  imagesizes="100vw"
  fetchpriority="high"
/>

        
        
          
        
      

Если ресурс на другом домене, добавьте <link rel="preconnect"> в секцию <head>, а ещё лучше держите его на основном домене.

Длительность загрузки ресурса

Скопировано

Для фотореалистичных LCP-изображений — hero-баннеров и обложек — SVG не подходит: используйте растровые форматы AVIF, WebP или JPEG.

Сжимайте и переводите картинки в современные форматы. Порядок предпочтения: AVIF (на ~50% легче JPEG, поддержка ~95%), далее WebP (на 25–35% легче, поддержка ~96%), и JPEG как запасной вариант для браузеров без поддержки современных форматов. Раздавайте через <picture> с несколькими <source>, чтобы браузер сам выбрал, что он поддерживает:

        
          
          <picture>  <!-- Самый предпочтительный формат -->  <source srcset="hero.avif" type="image/avif" />  <!-- Если AVIF не поддерживается -->  <source srcset="hero.webp" type="image/webp" />  <!-- Запасной вариант -->  <img    src="hero.jpg"    alt="Обложка"    width="1200"    height="630"    fetchpriority="high"  /></picture>
          <picture>
  <!-- Самый предпочтительный формат -->
  <source srcset="hero.avif" type="image/avif" />

  <!-- Если AVIF не поддерживается -->
  <source srcset="hero.webp" type="image/webp" />

  <!-- Запасной вариант -->
  <img
    src="hero.jpg"
    alt="Обложка"
    width="1200"
    height="630"
    fetchpriority="high"
  />
</picture>

        
        
          
        
      

Отдавайте адаптивные размеры через srcset и sizes: не грузите 4K-картинку на мобильные устройства.

        
          
          <picture>  <source    type="image/avif"    srcset="      hero-640.avif   640w,      hero-1280.avif 1280w,      hero-1920.avif 1920w    "    sizes="100vw"  />  <source    type="image/webp"    srcset="      hero-640.webp   640w,      hero-1280.webp 1280w,      hero-1920.webp 1920w    "    sizes="100vw"  />  <img    src="hero-1280.jpg"    srcset="      hero-640.jpg   640w,      hero-1280.jpg 1280w,      hero-1920.jpg 1920w    "    sizes="100vw"    alt="Обложка"    width="1280"    height="720"  /></picture>
          <picture>
  <source
    type="image/avif"
    srcset="
      hero-640.avif   640w,
      hero-1280.avif 1280w,
      hero-1920.avif 1920w
    "
    sizes="100vw"
  />

  <source
    type="image/webp"
    srcset="
      hero-640.webp   640w,
      hero-1280.webp 1280w,
      hero-1920.webp 1920w
    "
    sizes="100vw"
  />

  <img
    src="hero-1280.jpg"
    srcset="
      hero-640.jpg   640w,
      hero-1280.jpg 1280w,
      hero-1920.jpg 1920w
    "
    sizes="100vw"
    alt="Обложка"
    width="1280"
    height="720"
  />
</picture>

        
        
          
        
      

Используйте CDN и image CDN, которые на лету подбирают формат и размер под устройство пользователя. Современный image CDN автоматически выбирает подходящий формат и размер изображения: смотрит на Client Hints (заголовки, в которых браузер сам сообщает серверу ширину вьюпорта и плотность пикселей экрана), заголовок Save-Data и User-Agent: Chrome получит AVIF/WEBP, старый браузер — PNG/JPEG, без вручную написанных <picture>-развилок. Экономия по весу файла может составить 40–80%.

Настройте долгое кеширование (Cache-Control с большим max-age) для статических файлов — шрифтов, картинок и т. д. Не забудьте про query-параметры: сортируйте их и убирайте аналитические метки, не влияющие на содержимое страницы, — иначе они ломают кеш. И не отправляйте Set-Cookie вместе с кешируемыми ответами: большинство CDN не кеширует их вовсе.

Уберите конкуренцию за пропускную способность сети: понизьте fetchpriority второстепенным ресурсам, отложите сторонние скрипты через defer или async. Если на экране несколько картинок, но LCP-элементом станет только одна (например, первый слайд карусели), явно понизьте приоритет остальных через fetchpriority="low", чтобы они не отнимали пропускную способность у главного элемента.

Совсем маленькие изображения можно вставить как data URL прямо в HTML/CSS (data:image/...;base64,...). Так браузер получает картинку без отдельного сетевого запроса. Годится только для небольших иконок: base64 увеличивает вес файла примерно на треть и не кешируется отдельно от документа, поэтому для LCP-картинок это скорее исключение, чем правило.

Если LCP-элемент — это простая векторная графика (иконка, лого, иллюстрация без фотографий), вставляйте её инлайновым <svg> прямо в HTML, а не через <img src="pic.svg">. Тогда браузеру не придётся делать отдельный запрос за SVG-файлом — графика приедет вместе с HTML, что уменьшит задержку до её отображения. Перед отправкой обработайте свой SVG через SVGO (исходники на GitHub): он вычищает метаданные редактора, комментарии, лишние атрибуты и не влияющие на рендер данные, которые остаются после экспорта из графических редакторов. Для разовой оптимизации без установки есть веб-версия SVGOMG.

На практике этот этап редко оказывается узким местом: у большинства сайтов основную часть LCP съедают TTFB и задержки, а не сама загрузка файла. Прежде чем оптимизировать именно длительность загрузки, проверьте по полевым данным (собранным у реальных пользователей через Chrome UX Report (CrUX) или библиотеку web-vitals), действительно ли это ваше слабое место.

Задержка отрисовки

Скопировано

Сократите блокирующий CSS: вынесите критические стили как инлайновые, остальное грузите отложенно. Удалите неиспользуемые правила (проверьте через инструмент Coverage в DevTools). Некритичный файл можно асинхронно загрузить приёмом «preload, который после загрузки превращается в stylesheet». Браузер скачивает файл асинхронно, не блокируя рендер, а onload подключает стили, как только они готовы. <noscript> — запасной вариант для отключённого JS. У этого приёма есть свои компромиссы: страница может на мгновение мигнуть нестилизованным содержимым (FOUC), да и поддерживать такой код сложнее:

        
          
          <link  rel="preload"  href="non-critical.css"  as="style"  onload="this.onload=null;this.rel='stylesheet'"/><!-- Запасной вариант для браузеров с отключенным JavaScript --><noscript><link rel="stylesheet" href="non-critical.css" /></noscript>
          <link
  rel="preload"
  href="non-critical.css"
  as="style"
  onload="this.onload=null;this.rel='stylesheet'"
/>

<!-- Запасной вариант для браузеров с отключенным JavaScript -->
<noscript><link rel="stylesheet" href="non-critical.css" /></noscript>

        
        
          
        
      

Старайтесь не добавлять синхронный JavaScript в <head>: переносите в конец <body> страницы или добавляйте async или defer по возможности.

Рендерите на сервере (SSR) или используйте статическую генерацию (SSG), чтобы браузер получил LCP-контент сразу в HTML, а не строил его после выполнения JavaScript.

Следите, чтобы тяжёлые задачи на главном потоке не блокировали отрисовку сразу после загрузки картинки (смотрите про длинные задачи в статье про INP).

Не задерживайте отображение текста веб-шрифтами. Если LCP-элемент — текст, используйте font-display: swap, чтобы браузер сразу показал текст запасным шрифтом и подменил его на кастомный после загрузки, и предзагружайте критические шрифты через <link rel="preload">. Иначе браузер может отложить отображение текста до завершения загрузки шрифта.

Углублённая оптимизация

Скопировано

Если у вас многостраничный сайт (не одностраничное SPA-приложение), присмотритесь к Speculation Rules API. Он позволяет заранее предзагружать или полностью пререндерить наиболее вероятную следующую страницу, например, при наведении курсора на ссылку или на основе заранее заданных правил. При переходе пользователь практически мгновенно видит готовую страницу, а значение LCP для такой навигации становится очень маленьким, сравнимым с восстановлением страницы из bfcache — кеша, из которого браузер мгновенно достаёт уже отрисованную страницу целиком при переходе «назад»/«вперёд», без новой загрузки.

На момент написания статьи технология поддерживается только браузерами на базе Chromium (Chrome, Edge и другими) и пока недоступна в Safari и Firefox. Но следить за ней уже стоит, особенно если значительная часть вашей аудитории использует Chromium-браузеры.

        
          
          <script type="speculationrules">  {    "prerender": [      { "where": { "href_matches": "/*" }, "eagerness": "moderate" }    ]  }</script>
          <script type="speculationrules">
  {
    "prerender": [
      { "where": { "href_matches": "/*" }, "eagerness": "moderate" }
    ]
  }
</script>

        
        
          
        
      

eagerness задаёт, насколько рано браузер начнёт пререндерить страницу: immediate — сразу для всех подходящих ссылок, eager — как только пользователь навёл курсор, moderate — после недолгого наведения на ссылку, conservative — только когда пользователь начал клик. Чем раньше старт, тем выше шанс, что пререндер окажется зря потраченным, если пользователь передумает.