Оптимизация изображений для веба: где теряются мегабайты
Скриншот на 2,1 МБ ужимается до 102 КБ, а фотография теряет три четверти веса ещё до разговора о качестве сжатия. Разбираем порядок действий, при котором картинки перестают быть самой тяжёлой частью страницы.

Картинки обычно оказываются самой тяжёлой частью страницы, и почти всегда они тяжелее, чем нужно. Скриншот интерфейса на 2,1 МБ ужимается до 102 КБ без заметной глазу разницы, а фотография на 4,7 МБ теряет три четверти веса ещё до того, как вы дотронетесь до качества сжатия.
Разница между четырёхсекундной загрузкой и загрузкой меньше секунды обычно лежит именно здесь, а не в бандле скриптов. Ниже пошаговая оптимизация изображений: что делать первым, какой формат выбирать под какой тип картинки и почему кодировать современные форматы нужно на сборке, а не на лету.
Ключевые выводы
Первым делом уменьшайте размер в пикселях, а не качество: фотография 6000×4000 в карточке шириной 1200 пикселей это чистые потери. Один этот шаг убрал 77% веса тестового снимка.
Формат выбирается под тип картинки: фотографиям WebP или AVIF, скриншотам и интерфейсам сжатие без потерь, логотипам SVG, анимациям анимированный WebP вместо GIF.
AVIF при равном качестве весит вдвое меньше JPEG, но кодируется примерно в пятнадцать раз дольше, поэтому генерировать его нужно на этапе сборки.
Метаданные съёмки занимают от 10 до 30 КБ на файл и в вебе не нужны ни для чего.
Сжатие с потерями на тексте и резких границах даёт видимые артефакты: скриншоты и логотипы жмутся только без потерь, зато на 20–60%.
Шаг, который экономит больше всех остальных
Прежде чем подбирать кодек и качество, посмотрите на фактические размеры. Фотография 6000×4000 в карточке шириной 1200 CSS-пикселей отдаёт браузеру в двадцать пять раз больше пикселей, чем нужно обычному экрану, и вшестеро больше, чем нужно экрану с двойной плотностью.
В замерах автора чеклиста одно только уменьшение до реально отображаемого размера ужало тестовое изображение с 4,7 МБ до 1,1 МБ. Это минус 77% при нулевой потере качества: пиксели, которых не видно, просто перестали передаваться.
Как определить нужный размер:
Отправная точка это ширина контейнера в CSS-пикселях. На обычном экране она же и есть нужная ширина в физических пикселях, на плотных экранах нужно кратно больше: у части ноутбуков это двойка, у большинства современных смартфонов тройка. Поэтому правильный инструмент здесь не фиксированный множитель, а атрибут srcset с несколькими вариантами файла: браузер сам возьмёт тот, что соответствует плотности экрана посетителя.
Формат под тип изображения, а не один на всё
Самая частая ошибка — выбрать один формат и применять его ко всему подряд. Между фотографией с плавными градиентами и скриншотом с текстом и резкими границами разница принципиальная, и сжимаются они противоположными способами.
- Фотографии — WebP (на 30–50% меньше JPEG) или AVIF (ещё примерно на 30% меньше WebP).
- Скриншоты и элементы интерфейса — PNG или WebP без потерь.
- Логотипы и иконки — SVG: вектор весит копейки и масштабируется без предела.
- Анимации — анимированный WebP вместо GIF, это экономит 60–80% веса.
Отдельно про текст. Сжатие с потерями на буквах и резких границах создаёт заметные ореолы вокруг символов, поэтому скриншоты и логотипы обрабатываются только алгоритмами без потерь. Инструменты вроде oxipng ужимают такие файлы на 20–60%, не меняя при этом ни одного пикселя.
Конвертация и сжатие — это два разных действия
Перевод PNG в JPEG экономит место. Сжатие получившегося JPEG экономит ещё столько же, и про этот второй проход регулярно забывают. Для фотографий рабочий диапазон качества это 75–82%: от стопроцентного такая картинка визуально неотличима, а весит заметно меньше.
Метаданные, которые вы отдаёте бесплатно
В файле с камеры или из редактора лежат EXIF, координаты GPS, модель камеры и метки программы обработки. Для показа в браузере это бесполезный груз весом от 10 до 30 КБ на файл, за одним исключением: тег ориентации. Часть снимков с телефона хранит пиксели неповёрнутыми и полагается на него, поэтому поворот нужно применить к самим пикселям до того, как метаданные срежутся, иначе фотографии лягут набок. На каталоге из тысячи товарных снимков набегает до 30 МБ трафика ни за что, а координаты съёмки вдобавок могут оказаться данными, которые вы не собирались публиковать.
AVIF: что это и почему его нельзя кодировать на лету
AVIF (AV1 Image File Format) выпущен Alliance for Open Media в 2019 году и построен на внутрикадровом кодировании видеокодека AV1. По сути это инструменты сжатия одного кадра AV1, упакованные в самостоятельный контейнер для картинок.
Перед WebP у него два практических преимущества: выше степень сжатия при равном качестве и глубина цвета до 12 бит с поддержкой расширенного динамического диапазона, тогда как WebP ограничен восемью битами и обычным диапазоном. Достигается это за счёт заметно более богатого набора инструментов предсказания, унаследованного от видеокодека.
Сколько это в килобайтах
В сравнительном тесте брали пейзажный кадр 1920×1080 с градиентами неба, текстурой листвы и границами зданий, исходник в BMP весил 5,93 МБ. Целевое качество задавали по метрике структурного сходства: она сравнивает сжатую картинку с исходной, где единица означает полное совпадение, а 0,92 примерно соответствует порогу, за которым разницу перестают замечать глазом. Результаты:
На листве и градиентах неба разница видна и глазом: JPEG на низком битрейте показывает цветовые блоки, у AVIF артефактов практически нет. Отвечают за это многоуровневые фильтры внутри цикла кодирования, которые заглаживают границы блоков и восстанавливают детали текстур.
Плата за это — время. Кодирование AVIF примерно в пятнадцать раз дольше JPEG и вчетверо дольше WebP, потому что выбор разбиения и типа преобразования требует перебора с оптимизацией. Зато декодирование медленнее JPEG всего вдвое, и на просмотр это почти не влияет. Практический вывод прямой: генерируйте AVIF заранее, на этапе сборки. Сервисы доставки изображений кодируют его и по запросу, но только с кешированием результата, чтобы платить за кодирование один раз на вариант, а не на каждого посетителя. Чего делать нельзя, так это кодировать заново при каждом обращении.
Как отдавать современный формат и не потерять старые браузеры
Поддержка AVIF в браузерах составляет примерно 96%, но подстраховка всё равно нужна. Делается она тегом <picture>: браузер берёт первый формат, который понимает, и до остальных не доходит.
Атрибуты width и height здесь не декоративные: без них браузер не знает пропорций до загрузки файла и подвёрстывает страницу заново, когда картинка приходит. Это тот самый прыжок содержимого, который портит метрику визуальной стабильности. Атрибут loading=lazy убирает изображения за пределами первого экрана из очереди, конкурирующей за отрисовку главного элемента.
Именно за пределами: на картинку первого экрана его ставить нельзя. Отложенная загрузка задержит ровно тот элемент, скорость появления которого и меряет метрика отрисовки основного содержимого. Там уместен противоположный по смыслу fetchpriority=high.
Сжатие в браузере: почему это вообще возможно
Браузер умеет декодировать, преобразовывать и кодировать изображение прямо на устройстве, не отправляя файл никуда. Для товарных снимков, клиентских материалов и прочего чувствительного это снимает целый шаг из конвейера вместе с вопросом, где полежит копия.
Речь здесь уже не про ассеты вашего сайта, которые готовятся на сборке. Речь про картинки, которые в браузер приносит сам пользователь: форма загрузки в личном кабинете, объявление на маркетплейсе, вложение в заявку. Сжать их до отправки на сервер можно прямо во вкладке, и это снимает и трафик, и вопрос о том, где полежит оригинал.
Автор одного из таких инструментов разобрал устройство своего конвейера, и его опыт пригодится всем, кто выносит тяжёлые вычисления во фронтенд.
Вся тяжёлая работа уходит из главного потока
Перекодирование снимка 4000×4000 нагружает процессор настолько, что в главном потоке интерфейс замирает, а браузер показывает предупреждение о зависшей странице. Поэтому весь конвейер живёт в Web Worker: главный поток отвечает за выбор файлов, превью и состояние, а рабочий поток декодирует через createImageBitmap, применяет преобразования и кодирует через OffscreenCanvas. Именно через него, а не через canvas.toBlob: последний это метод DOM-элемента, а DOM в рабочем потоке отсутствует, и нужный метод там называется convertToBlob.
Важная деталь производительности: передавать данные между потоками нужно как ArrayBuffer через механизм передачи владения, а не структурным клонированием. На пачке из тридцати изображений это разница между мгновенным откликом и лишними полусекундами нагрузки на сборщик мусора для каждого файла.
Три ошибки, которые ищутся дольше всего
Рабочий поток не имеет доступа к window. Причём не только напрямую: достаточно подключить библиотеку, которая трогает window на верхнем уровне модуля. В консоли разработчика появляется ошибка обращения к неопределённому объекту, а вот в интерфейсе не появляется ничего: если сбой рабочего потока никак не показан пользователю, тот видит вечный спиннер. Лечится ревизией всех импортов рабочего потока: библиотеки, завязанные на браузерное окружение, остаются в главном потоке и передают в рабочий поток уже готовые данные.
Округление сломало масштабирование. Быстрый путь изменения размера использовал округлённый шаг по пикселям. На больших изображениях округление уводило исходную координату за пределы строки, и картинка выходила разрезанной по горизонтали с чёрной мозаикой внизу. Помогли точные дробные коэффициенты и ограничение по границам в каждом ручном проходе по пикселям.
Асинхронный обработчик сообщений перепутал порядок. Обработчик стал ждать преобразование уже после кодирования, а асинхронные обработчики порядок не сохраняют: состояние поворота разъехалось с готовым изображением. Полностью синхронным такой обработчик не сделать, декодирование и кодирование асинхронны по своей природе. Работает другое: обрабатывать сообщения строго по одному, не начиная следующее до завершения предыдущего, и хранить состояние вроде угла поворота рядом с данными конкретного сообщения, а не в общей переменной.
Заголовки безопасности ломают рабочие потоки молча:
Блокирует загрузку скрипта заголовок Cross-Origin-Embedder-Policy: он требует, чтобы каждый сторонний ресурс отдавал разрешающий заголовок, иначе браузер молча его отбрасывает. Cross-Origin-Opener-Policy на это не влияет, но включают их обычно парой, поэтому и ломается всё в одном релизе. Любое изменение заголовков безопасности проверяйте на настоящем развёрнутом стенде, а не только на машине разработчика.
Что это даёт в цифрах
Скриншот 1920×1080 проходит такой путь: исходный PNG весит 2,1 МБ, после уменьшения и перевода в WebP с качеством 75% — 186 КБ, то есть минус 91%. Тот же кадр в AVIF занимает 102 КБ, минус 95% от исходного.
Именно эти проценты и отделяют страницу, которая грузится четыре секунды, от страницы, которая укладывается в одну.
Чеклист: пройтись по своим картинкам
- 01Сверьте пиксели с вёрсткой
Откройте панель сети и сравните натуральный размер картинок с размером контейнера. Если самый крупный вариант заметно превышает то, что нужно даже плотному экрану, уменьшайте до начала любых других работ.
- 02Разделите картинки по типам
Фотографии, скриншоты, логотипы и анимации обрабатываются разными алгоритмами. Один формат на весь проект гарантированно проигрывает и в весе, и в качестве.
- 03Снимите метаданные
Уберите EXIF и координаты при сборке. Это от 10 до 30 КБ на файл и заодно данные, которые вы вряд ли собирались публиковать.
- 04Добавьте второй проход сжатия
После конвертации прогоните файл через сжатие: для фотографий качество 75–82%, для текстовых изображений только алгоритмы без потерь.
- 05Перенесите генерацию форматов в сборку
AVIF кодируется примерно в пятнадцать раз дольше JPEG, поэтому его место в конвейере сборки, а не в обработчике запроса.
- 06Пропишите размеры и отложенную загрузку
Атрибуты ширины и высоты убирают прыжок вёрстки, а отложенная загрузка снимает конкуренцию за отрисовку первого экрана.
Часто задаваемые вопросы
Что даёт больше всего экономии при оптимизации картинок?
Уменьшение размера в пикселях до фактически отображаемого в вёрстке. Фотография 6000×4000 в карточке шириной 1200 пикселей отдаёт браузеру в двадцать пять раз больше пикселей, чем нужно обычному экрану. В замерах автора чеклиста один этот шаг убрал 77% веса, с 4,7 МБ до 1,1 МБ, при нулевой потере качества. Подбор кодека и качества имеет смысл только после него.
AVIF или WebP: что выбрать?
AVIF даёт файл вдвое меньше JPEG при равном качестве, поддерживает 12 бит и расширенный динамический диапазон, но кодируется примерно в пятнадцать раз дольше JPEG и вчетверо дольше WebP. Если изображения готовятся заранее, на сборке, берите AVIF с запасным WebP через тег picture. Если картинка создаётся в момент запроса, WebP практичнее: кодируется быстрее и меньше нагружает сервер.
Насколько AVIF меньше JPEG в реальном тесте?
На кадре 1920×1080 при структурном сходстве около 0,92 JPEG с качеством 85 занял 420 КБ, а AVIF — 210 КБ, то есть ровно вдвое меньше и на 29,5% меньше WebP. PNG без потерь на том же кадре весил 4,82 МБ и для фотографий не подходит.
Почему нельзя сжимать скриншоты как фотографии?
Сжатие с потерями размывает резкие границы и создаёт заметные ореолы вокруг букв, из-за чего текст на скриншоте становится грязным и хуже читается. Для скриншотов, интерфейсов и логотипов применяют алгоритмы без потерь, например oxipng: они уменьшают файл на 20–60%, не меняя при этом ни одного пикселя изображения.
Нужны ли атрибуты width и height, если размеры заданы в CSS?
Да, нужны в любом случае. Атрибуты в разметке сообщают браузеру пропорции картинки ещё до загрузки файла, поэтому место под неё резервируется заранее. Без них страница подвёрстывается заново в момент загрузки изображения, и содержимое прыгает под курсором у читателя, а это прямо ухудшает метрику визуальной стабильности.
Что забрать с собой
Порядок действий важнее набора инструментов. Сначала пиксели, потом формат под тип изображения, потом второй проход сжатия, и только затем разговор про кодеки нового поколения. Обратный порядок даёт красивую строчку в отчёте и почти никакого эффекта на реальной странице.
Материалы разбора: чеклист оптимизации с замерами, техническое устройство AVIF и сравнительный тест форматов и разбор браузерного конвейера сжатия.
Откройте свою главную страницу с открытой панелью сети и отсортируйте запросы по размеру. Первые три строки почти наверняка окажутся картинками, и это ваша ближайшая оптимизация изображений: уменьшить их вдвое реально за один вечер.








