Вернуться в блог
Технологии

Оптимизация картинок сайта 2026: 8 стратегий для Core Web Vitals

Опубликовано8 min read
Поделиться

Картинки — узкое место #1 в производительности веба

С точки зрения архитектуры, картинки занимают подавляющую долю веса современных страниц. По статистике HTTP Archive за декабрь 2025, медианный вес десктопной страницы ~2 312 КБ, из которых ~1 026 КБ — картинки, почти 44.4%. На мобильном ещё выше: ~47%.

Значит одно: если выбираете один вектор оптимизации — это картинки.

Ещё важнее: картинки прямо влияют на Google Core Web Vitals, особенно LCP (Largest Contentful Paint). По CrUX-отчёту Google, у 70%+ страниц элемент LCP — картинка или блок с background-image. LCP > 2.5 сек → «требуется улучшение», > 4 сек → «плохо», напрямую режет SEO.

Разберём каждый слой оптимизации.

Реальный случай из практики: LCP взлетел с 1.8s до 6.2s

Сразу к делу: в прошлом году вёл клиентский проект, LCP улетел из зелёной зоны (1.8s) в красную (6.2s), и всё из-за одной картинки на главной. Если честно, я сначала грешил на JS-бандл — три дня дебажил бесполезно. Оказалось — новый дизайнер залил на главную hero-картинку 4.5 МБ, PNG 1920×1080, без сжатия, без атрибута loading, без fetchpriority и даже без CDN. По замерам, одна эта картинка в одиночку утопила LCP.

После трёх изменений LCP вернулся к 1.9s:

  1. PNG → WebP, пожал до 180 КБ (минус 96%)
  2. Добавил fetchpriority="high" и <link rel="preload">
  3. Перенёс на Cloudflare R2 с Transform Rules и AVIF fallback

На эти грабли наступают многие. Скажу прямо: hero-картинка на главной — это ахиллесова пята LCP, одно слабое звено — и весь Core Web Vitals красный. Все разделы ниже — выжимка из таких кейсов, не переписывание документации.

Lazy loading: не грузить невидимое

Нативный HTML

Проще всего — атрибут loading="lazy":

<img src="photo.webp" loading="lazy" alt="Пример" width="800" height="600">

Браузер запрашивает картинку при приближении к viewport. В Chrome — на расстоянии ~1250 px (десктоп) или ~2500 px (медленное соединение).

Важно: первый экран (above the fold) — не ставить lazy loading, это ломает LCP. Там нужен loading="eager" (по умолчанию) + fetchpriority="high" для приоритета.

JavaScript: Intersection Observer

Когда нужен точный контроль — Intersection Observer:

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      observer.unobserve(img);
    }
  });
}, { rootMargin: '200px' });

document.querySelectorAll('img[data-src]').forEach(img => observer.observe(img));

Плюсы: кастомный rootMargin, fade-in анимация, LQIP (Low-Quality Image Placeholder).

Сравнение

Параметр Нативный loading="lazy" Intersection Observer
Сложность Минимум (атрибут) Средняя (JS)
Поддержка браузеров 95%+ (2026) 97%+
Контроль расстояния Нет Да (rootMargin)
LQIP Нет Да
SEO-дружественность Да (нативно) Нужен noscript-fallback
Риск «lazy на first-fold» Есть (вручную исключать) Можно программно

Стратегия: нативный для 95% случаев, Observer — только когда нужен LQIP или тонкий контроль.

Responsive images: устройству только нужный размер

Hero-картинка 3000 px шириной на смартфоне бессмысленна — экрану достаточно 750 px. Без responsive images телефоны качают избыточные в 4 раза файлы.

srcset + sizes

<img
  srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w, hero-1600.webp 1600w"
  sizes="(max-width: 600px) 100vw, (max-width: 1200px) 50vw, 800px"
  src="hero-800.webp"
  alt="Обложка главной страницы"
>

Браузер сам выбирает оптимальный размер по ширине экрана и density. sizes говорит, сколько места картинка займёт в разных брейкпоинтах.

<picture>: форматы и art direction

<picture>
  <source srcset="hero.avif" type="image/avif">
  <source srcset="hero.webp" type="image/webp">
  <img src="hero.jpg" alt="Обложка">
</picture>

<picture> позволяет отдавать разные форматы под разные браузеры: AVIF тем, кто умеет, WebP — кто умеет WebP, JPEG — всем остальным.

Стратегия выбора формата

Выбор формата прямо влияет на вес и качество. Тест на одной картинке 1920×1080 при визуально одинаковом качестве:

Формат Вес (отн. JPEG) Поддержка Alpha Анимация Применение
JPEG 100% base 100% Нет Нет Максимальная совместимость
WebP ~70% 97%+ Да Да Рекомендуемый дефолт
AVIF ~50% 92%+ Да Да Максимальная экономия
PNG 300–500% 100% Да Нет Графика с прозрачностью
SVG ~0 (векторный) 100% Да Да Иконки, лого, диаграммы

Стратегия: прогрессивная деградация AVIF → WebP → JPEG. <picture> автоматически выбирает.

CDN и стратегия кеша

Зачем CDN

От сервера в Москве к пользователю в Новосибирске по оптике — физическая задержка ~40 мс (одна сторона). TCP handshake + TLS + HTTP — TTFB может быть > 200 мс. Для пользователя в Стамбуле или Хельсинки — ещё хуже.

CDN кеширует картинки на edge-нодах. Пользователь обслуживается с ближайшего узла. TTFB с 200 мс → 20–50 мс.

Заголовки кеша

Cache-Control: public, max-age=31536000, immutable

Картинки обычно не меняются — год кеша плюс immutable избавляет от revalidation. Обновлять через хеш в имени файла.

Специализированные image CDN

Cloudflare Images, Imgix, Cloudinary — не только CDN-кеширование, но и:

  • Перекодирование на лету (Accept-заголовок → AVIF/WebP/JPEG)
  • Ресайз по URL-параметрам
  • Адаптивное качество под скорость канала

Российские image CDN

Для российской аудитории критична скорость из РФ. Варианты:

  • Yandex Object Storage + Yandex CDN — edge-ноды в РФ, низкая латентность
  • Selectel + Selectel CDN — хорошее покрытие РФ и СНГ
  • Cloudflare (работает в РФ, но возможны эпизодические замедления у отдельных провайдеров)
  • VK Cloud + CDN — официальное решение VK, хороший пиринг с Ростелекомом

Для ФЗ-152-зависимых проектов (пользовательские фото) — только российские провайдеры.

Пайплайн сжатия картинок

Полный серверный пайплайн включает:

  1. Сжатие на upload — Sharp (Node.js) или Pillow (Python) на стороне сервера
  2. Мультиразмер — 3–5 вариантов (400w, 800w, 1200w, 1600w, original)
  3. Мультиформат — AVIF, WebP, JPEG для каждого размера
  4. Настройка качества — JPEG 75–85, WebP 75–80, AVIF 60–70
  5. Очистка метаданных — снижает вес + защита приватности

Для фото 1920×1080 после пайплайна мобильный пользователь качает 400w AVIF на 30 КБ вместо оригинальных 2 МБ — экономия 98.5% трафика.

Core Web Vitals: цели

LCP (Largest Contentful Paint)

  • Хорошо: ≤ 2.5 с
  • Требуется улучшение: 2.5–4 с
  • Плохо: > 4 с

Приёмы для картинки LCP:

  1. fetchpriority="high" — приоритет загрузки
  2. <link rel="preload"> — предзагрузка
  3. Никакого lazy loading на LCP
  4. Кеш CDN должен быть прогрет

CLS (Cumulative Layout Shift)

Картинка без заданного размера двигает контент. Решение: всегда указывайте width и height:

<img src="photo.webp" width="800" height="600" alt="Пример">

Так браузер резервирует пропорцию до загрузки.

Чек-лист оптимизации

Перед выкатом в прод:

  • Above-fold: fetchpriority="high", без lazy; below-fold: loading="lazy"
  • У всех <img> есть width и height
  • <picture> с AVIF/WebP/JPEG
  • srcset + sizes с мульти-размерами
  • Картинки сжаты (JPEG 75–85, WebP 75–80)
  • CDN включён, Cache-Control: max-age=31536000, immutable
  • EXIF вычищен
  • LCP-картинка с <link rel="preload">
  • PageSpeed Insights: LCP ≤ 2.5 с
  • Проверка в Яндекс.Вебмастере (для РФ-проектов)

Когда нужен свой пайплайн, а когда достаточно хостинга

Если проект маленький (личный блог, MVP), строить свой пайплайн — оверкилл. Image hosting закрывает 80% задач.

lpic.cc отдаёт без регистрации, с CDN и защитой паролем. Для блогеров и малых сайтов — рабочий вариант, экономит месяцы разработки. На практике, правда, lpic.cc не даёт API и on-fly трансформаций — для программного batch-загрузчика или динамического ресайза смотрите в сторону Cloudinary, Imgix (или Yandex Object Storage + Workers для РФ).

Для enterprise — собственный пайплайн: Sharp на backend + Cloudflare R2 (или Yandex Object Storage) + CDN. Даёт полный контроль над качеством, форматом, размером и стоимостью.

Частые вопросы

Q1. Lazy loading вредит SEO?

Нет. Googlebot выполняет JS и эмулирует скролл — индексирует картинки с loading="lazy". Для кастомного JS-lazy-loading добавьте <noscript>-fallback, чтобы поисковые боты уверенно находили URL.

Q2. WebP или AVIF — что предпочесть?

По сжатию AVIF впереди (~50% от JPEG vs 70% у WebP). Но AVIF медленнее кодируется — не годится для real-time. Оптимальная стратегия — <picture> с обоими форматами, браузер сам выберет. Если выбирать один — WebP (97% поддержка vs 92% у AVIF) безопаснее.

Q3. Платные image CDN стоят денег?

Зависит от трафика. До 100 тыс. PV/мес — бесплатный Cloudflare обычно справляется. Выше — Imgix/Cloudinary с автоконверсией форматов и ресайзом окупаются экономией трафика. Для РФ-аудитории — Yandex Cloud CDN или Selectel CDN.

Q4. Сжатие убивает качество?

Lossy всегда что-то теряет, но в разумных параметрах (JPEG 75–85, WebP 75–80) глаз различий не видит. Объективная метрика — SSIM: > 0.95 — большинство пользователей не заметят.

Q5. У меня уже CDN, нужна ли оптимизация картинок?

Нужна. CDN решает «расстояние», не «объём». Несжатая картинка 5 МБ и через CDN остаётся 5 МБ. Сжатие + формат + ресайз убирают 80–95% трафика — эффект куда больше, чем просто CDN.

Источники

#веб-производительность#оптимизация картинок#lazy loading#responsive images#CDN#Core Web Vitals
H

Автор

Harvey Zhang

Сооснователь и технический директор

Бэкенд-инженер, ставший архитектором, предлагает глубокий технический анализ инфраструктуры хостинга изображений.