Оптимизация изображений в 2026: AVIF, WebP, srcset и LCP

Как ускорить LCP-картинки в 2026: AVIF с фолбэком на WebP, корректный srcset/sizes, fetchpriority, CLS-защита и пайплайн на Sharp 0.34.

AVIF, WebP, srcset и LCP: гайд 2026

Обновлено: 17 июня 2026

Оптимизация изображений в 2026 году сводится к трём вещам: формат AVIF с фолбэком на WebP, корректный srcset c sizes и явная приоритизация LCP-картинки через fetchpriority="high". Картинки занимают 40–60% веса средней страницы и почти всегда оказываются Largest Contentful Paint элементом, поэтому каждый сэкономленный килобайт и каждая миллисекунда раньше старта загрузки напрямую улучшают Core Web Vitals. В этом гайде собрал рабочие команды, разметку и трассы из DevTools, которые я применяю на продакшене (некоторые приёмы вытащил из боли последних трёх релизов).

  • AVIF даёт на 30–50% меньше размер по сравнению с WebP при том же визуальном качестве; поддержка браузеров в 2026 году достигла 96.3% по Can I Use.
  • LCP-изображение должно загружаться через <img> с fetchpriority="high" и без loading="lazy". Это единственный способ обогнать CSS и JS в очереди приоритетов.
  • Атрибуты width и height на каждом <img> обязательны: они резервируют место и убирают CLS до того, как картинка успеет скачаться.
  • Адаптивные изображения через <picture> с тремя форматами (AVIF, WebP, JPEG) экономят 200–600 КБ на мобильных устройствах.
  • Sharp 0.34 и Squoosh CLI кодируют AVIF в 4–6 раз быстрее, чем libavif напрямую, что меняет экономику CI-пайплайна.
  • Декодирование больших картинок блокирует main thread; decoding="async" и content-visibility: auto переносят работу с него.

AVIF против WebP в 2026: что выбрать

Короткий ответ: AVIF как основной формат, WebP как фолбэк, JPEG/PNG как последняя ступень. По состоянию на июнь 2026 поддержка AVIF в браузерах составляет 96.3% (Chrome 85+, Firefox 113+, Safari 16+, включая стабильную поддержку на iOS 16+). WebP остаётся в 99.1% браузеров и нужен для устаревших iOS 15 и редких корпоративных Edge-сборок.

На моих стендах AVIF выигрывает у WebP по размеру при равном SSIM ≥ 0.97 примерно на 38% для фотографий и на 22% для иллюстраций с плоскими цветами. По сравнению с JPEG (q=80) AVIF при cq-level=28 даёт около 55% экономии. Цена за это: кодирование. cavif и libavif при пресете effort=4 работают в 6–10 раз медленнее cwebp; это надо учитывать в CI и предпочесть Sharp 0.34, который завернул AVIF в нативные bindings с SIMD.

ПараметрAVIFWebPJPEG (mozjpeg)
Поддержка браузеров (2026)96.3%99.1%100%
Размер vs JPEG q=80−55%−30%baseline
Скорость кодированиямедленнобыстроочень быстро
Альфа-каналдаданет
HDR / широкий цветда (10/12 бит)нетнет
Прогрессивное декодированиеданетда
Лучшее применениефото, hero, LCPUI-графикауниверсальный фолбэк

Практический рецепт: всегда генерируйте все три формата и отдавайте через <picture>. Не пытайтесь обойтись «только AVIF». Даже 3–4% посетителей без поддержки увидят сломанное изображение, и это будет ровно то подмножество, которое жалуется громче всех.

Как ускорить загрузку LCP-изображения

LCP-картинка, по сути, главная мишень оптимизации. На большинстве лендингов это hero-изображение, которое попадает в viewport за первые 600–1200 мс. По данным web.dev по LCP, разница между «хорошим» (≤2.5 с) и «плохим» (>4 с) LCP в 78% случаев объясняется задержкой обнаружения и старта загрузки именно этого ресурса, а не его размером.

Минимальный рабочий набор для LCP-изображения в 2026 году:

<!-- В head: для критичных hero-картинок, известных на этапе HTML -->
<link rel="preload"
      as="image"
      href="/hero-1280.avif"
      imagesrcset="/hero-640.avif 640w, /hero-1280.avif 1280w, /hero-1920.avif 1920w"
      imagesizes="100vw"
      fetchpriority="high"
      type="image/avif">

<!-- В разметке -->
<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"
       width="1920" height="1080"
       alt="Описание для скринридеров"
       fetchpriority="high"
       decoding="async">
</picture>

Три критичные детали, которые часто упускают. Первая: fetchpriority="high" ставится и на <link rel="preload">, и на <img>, потому что это два разных события в очереди браузера. Вторая: никогда не вешайте loading="lazy" на LCP-картинку, даже если она «как бы ниже фолда», иначе браузер отложит загрузку до layout-этапа и LCP уедет на 300–800 мс. Третья: type в preload обязателен, иначе Chrome дублирует загрузку AVIF и JPEG.

Если вы уже работаете с заголовками сервера, оптимальная пара такая: Preload Link плюс 103 Early Hints. Я подробнее разбирал это в материале про приоритизацию загрузки ресурсов и Early Hints; для LCP-изображений это даёт ещё 50–150 мс выигрыша на холодном TLS.

srcset и sizes: правильная разметка

Адаптивные изображения экономят больше всего трафика и времени декодирования. Мобильному устройству с экраном 390 CSS px не нужна картинка 1920 px (она сожжёт 250–500 мс на скачивание и декодирование). Правильный srcset + sizes позволяет браузеру выбрать ресурс под фактический размер контейнера и DPR.

Базовая семантика. srcset описывает доступные варианты с их естественной шириной (w-дескриптор). sizes описывает, как контейнер картинки будет занимать viewport. Браузер умножает sizes на devicePixelRatio, находит ближайший вариант из srcset и качает только его.

<!-- Картинка в двухколоночном лейауте -->
<img src="/p-800.avif"
     srcset="/p-400.avif 400w,
             /p-800.avif 800w,
             /p-1200.avif 1200w,
             /p-1600.avif 1600w"
     sizes="(min-width: 1024px) 50vw,
            (min-width: 640px) 75vw,
            100vw"
     width="800" height="600"
     alt="Карточка продукта">

Типичная ошибка: sizes="100vw" у картинки, которая на десктопе занимает половину контейнера. Браузер качает в два раза больше, чем нужно. Честно говоря, я сам ловил такие случаи на собственных проектах через RespImageLint; он находит расхождение между объявленным sizes и реальной шириной элемента на разных breakpoint-ах.

Правило плотности через дескриптор x (1x, 2x, 3x) используйте только для иконок и UI-графики, где размер фиксирован. Для всего остального используйте дескриптор w, иначе вы оптимизируете под одно устройство.

Элемент picture и фолбэки форматов

Элемент <picture> в 2026 году выполняет две задачи: фолбэки форматов и art direction (разная композиция для разных экранов). Браузер выбирает первый <source>, у которого type поддерживается и media подходит. Если ни один не подошёл, отдаёт <img>.

<picture>
  <!-- Art direction: на узких экранах квадратный кроп -->
  <source media="(max-width: 640px)"
          type="image/avif"
          srcset="/hero-square-640.avif 640w, /hero-square-1280.avif 1280w"
          sizes="100vw">
  <source media="(max-width: 640px)"
          type="image/webp"
          srcset="/hero-square-640.webp 640w, /hero-square-1280.webp 1280w"
          sizes="100vw">
  <!-- Десктоп: широкий кроп -->
  <source type="image/avif"
          srcset="/hero-wide-1280.avif 1280w, /hero-wide-1920.avif 1920w"
          sizes="100vw">
  <source type="image/webp"
          srcset="/hero-wide-1280.webp 1280w, /hero-wide-1920.webp 1920w"
          sizes="100vw">
  <img src="/hero-wide-1280.jpg"
       width="1920" height="1080"
       alt="..."
       fetchpriority="high">
</picture>

Важная деталь: width и height на <img> внутри <picture> с art direction должны соответствовать соотношению сторон того варианта, который реально будет показан в большинстве сессий. Иначе CLS на медленных сетях достигает 0.2+. Для двух разных aspect-ratio используйте aspect-ratio в CSS вместе с media query.

Предотвращение CLS: width, height и aspect-ratio

Cumulative Layout Shift на изображениях возникает, когда браузер до загрузки картинки не знает её итоговых размеров и схлопывает контейнер в 0. Когда картинка приезжает, контент ниже прыгает. Решение известно с 2020 года, и тем не менее половина команд до сих пор пропускает шаг. Я сам забыл это на одном PR в 2024-м и получил CLS 0.31 в проде, что стоило отдельного откатного релиза.

Правильно так:

<!-- Атрибуты width и height задают соотношение сторон -->
<img src="/photo.avif"
     width="1200"
     height="800"
     alt="...">

/* CSS оставляет картинку отзывчивой, не теряя aspect-ratio */
img {
  max-width: 100%;
  height: auto;
}

Современный браузер вычисляет aspect-ratio: 1200 / 800 из атрибутов и резервирует место до загрузки. Если картинка обрезается через CSS (object-fit: cover) и реальные размеры не совпадают с атрибутами, всё равно используйте те значения, которые соответствуют визуальному прямоугольнику в layout. CLS от изображений напрямую связан с другими CLS-источниками, особенно со шрифтами; я разбирал это в гайде про оптимизацию шрифтов и устранение CLS.

Lazy loading: когда применять, а когда нет

Атрибут loading="lazy" ставится на все картинки ниже первого экрана. Это нативный механизм, и Chrome, Firefox и Safari реализуют его через IntersectionObserver под капотом, без всякого JavaScript. Эффект: hero и видимый контент скачиваются первыми, а изображения в футере и нижних секциях откладываются до приближения к viewport.

<!-- Hero: не lazy! -->
<img src="/hero.avif" fetchpriority="high" width="1920" height="1080" alt="...">

<!-- Ниже фолда: lazy -->
<img src="/section-2.avif" loading="lazy" decoding="async" width="800" height="600" alt="...">

Три правила, которые я применяю строго:

  1. Никогда не ленизируйте LCP-картинку. Браузер откладывает её обнаружение до layout-фазы, что добавляет 200–800 мс к LCP.
  2. Не ленизируйте первые 1–2 изображения галереи, если они в viewport на типичном экране 1366×768.
  3. Для iframe используйте loading="lazy" на всех встраиваниях (YouTube, карты, виджеты) без исключений. Часто это самая большая экономия CPU.

Image CDN или Sharp в CI: что выбрать

Есть два главных подхода к генерации вариантов: статические в CI-пайплайне или динамические через image CDN. Я применяю оба, и выбор зависит от количества SKU и частоты обновления.

Статический пайплайн на Sharp 0.34 — это 30–60 строк кода, ноль внешних зависимостей и полностью кэшируемый CDN-слой. Подходит для контентных сайтов, документации, лендингов с десятками-сотнями изображений:

// build/optimize-images.mjs
import sharp from 'sharp';
import { readdir, mkdir } from 'node:fs/promises';
import { join, extname, basename } from 'node:path';

const WIDTHS = [400, 800, 1200, 1600, 1920];
const SRC = './src/images';
const OUT = './public/img';

const files = (await readdir(SRC)).filter(f => /\.(jpg|png)$/i.test(f));

await mkdir(OUT, { recursive: true });

for (const file of files) {
  const name = basename(file, extname(file));
  const input = sharp(join(SRC, file));
  const meta = await input.metadata();

  for (const w of WIDTHS) {
    if (w > meta.width) continue;
    const resized = input.clone().resize({ width: w });
    await Promise.all([
      resized.clone().avif({ quality: 50, effort: 6 })
        .toFile(join(OUT, `${name}-${w}.avif`)),
      resized.clone().webp({ quality: 80, effort: 5 })
        .toFile(join(OUT, `${name}-${w}.webp`)),
      resized.clone().jpeg({ quality: 82, mozjpeg: true, progressive: true })
        .toFile(join(OUT, `${name}-${w}.jpg`)),
    ]);
  }
}

На моих проектах этот скрипт обрабатывает 200 изображений за 90–120 секунд на 8-ядерной CI-машине. Sharp параллелит на native потоках, не упираясь в Node event loop.

Динамический image CDN (Cloudinary, ImageKit, Bunny Optimizer, Cloudflare Images) выигрывает там, где изображений тысячи и они приходят от пользователей: маркетплейсы, UGC-платформы. Цена: $0.5–3 за 1000 трансформаций плюс CDN-трафик. Архитектурно это всегда дороже статики, но снимает с команды задачу оркестрации.

Декодирование и рендеринг: вторая половина бюджета

Скачать картинку — это половина работы. Декодирование AVIF большого разрешения занимает 30–120 мс на main thread мобильного устройства среднего класса. По нашим трассам в Performance, это часто вылезает в Long Animation Frames и портит INP. Атрибут decoding="async" просит браузер декодировать вне main thread и не показывать картинку, пока она не готова. Это убирает janks при скролле:

<img src="/photo.avif"
     decoding="async"
     loading="lazy"
     width="800" height="600"
     alt="...">

Для длинных страниц с галереями применяйте CSS content-visibility: auto, чтобы браузер пропускал layout и paint для секций вне viewport. На страницах с 50+ карточек это даёт ускорение first paint в 3–7 раз; детали в материале про content-visibility и Containment API. Сочетание decoding="async" плюс content-visibility: auto плюс loading="lazy" — это стандартный стек для длинных лент в 2026 году.

Как замерить эффект и поймать регрессии

Измеряйте до и после в трёх местах: лабораторно (Lighthouse 12 / PageSpeed Insights), на реальных пользователях (RUM через web-vitals.js или CrUX) и в CI на каждый PR.

Минимальный snippet для RUM-измерения LCP с разбивкой по элементу:

import { onLCP } from 'web-vitals/attribution';

onLCP((metric) => {
  const { element, url, loadTime, renderTime, loadDelay } = metric.attribution;
  navigator.sendBeacon('/rum', JSON.stringify({
    value: metric.value,
    rating: metric.rating,
    elementSelector: element,
    elementUrl: url,
    loadDelay,
    loadTime,
    renderTime,
  }));
});

Что смотреть в трассах:

  • LCP element URL в RUM. Если URL «гуляет» между релизами, значит у вас не одна LCP-картинка, а несколько, и кэш отдыхает.
  • loadDelay. Любое значение выше 100 мс говорит о пропущенном preload или забытом fetchpriority.
  • renderDelay. Выше 200 мс значит, что браузер не успел распаковать картинку до paint. Проверьте размер AVIF (часто overcompressed, отсюда медленное декодирование) и decoding="async".

В CI прогоняйте Lighthouse через @unlighthouse/cli на превью-деплое каждого PR с порогами: LCP ≤ 2.0 s, CLS ≤ 0.05, размер главной картинки ≤ 120 КБ. Это ловит 90% регрессий до релиза.

Часто задаваемые вопросы

Какой формат изображения лучше всего использовать в 2026 году?

AVIF как основной формат для всех новых сайтов, WebP как фолбэк для 3–4% устаревших браузеров, JPEG/PNG как универсальный последний рубеж. Отдавайте все три через <picture>, и браузер сам выберет первый поддерживаемый.

Почему моя LCP-картинка загружается медленно, даже если она маленькая?

В 95% случаев проблема не в размере файла, а в задержке обнаружения. Браузер начинает качать LCP-картинку только после парсинга HTML и расчёта layout. Добавьте <link rel="preload" as="image" fetchpriority="high"> в head и fetchpriority="high" на сам <img>.

Нужно ли указывать width и height, если я задаю размер через CSS?

Да, обязательно. Атрибуты width и height используются браузером для вычисления aspect-ratio и резервирования места до загрузки картинки. Без них вы получите Cumulative Layout Shift, даже если CSS правильный.

Можно ли применять loading="lazy" ко всем изображениям сразу?

Нет. Lazy-loading LCP-картинки и изображений выше первого экрана откладывает их обнаружение до layout-фазы и ухудшает LCP на 200–800 мс. Применяйте loading="lazy" только к изображениям, которые гарантированно ниже фолда на всех устройствах.

Что лучше для оптимизации изображений: image CDN или статика в CI?

Для контентных сайтов и лендингов с сотнями изображений лучше статика на Sharp в CI: предсказуемая стоимость, 100% кэшируемость, ноль внешних зависимостей. Для UGC-платформ и маркетплейсов с тысячами картинок и динамическими трансформациями подойдёт image CDN типа Cloudflare Images или Cloudinary.

Как уменьшить размер изображений без потери качества?

Кодируйте AVIF с quality: 50, effort: 6 через Sharp 0.34. Это даёт визуально неотличимое от исходника качество при размере на 55% меньше JPEG q=80. Дополнительно: ресайз до реально нужной ширины (не больше 2× от CSS-размера) и использование srcset с дескрипторами w.

Alex Petrov
Об авторе Alex Petrov

Web performance engineer who treats every millisecond as a personal challenge. Has profiled more sites than he can count.