Загрузить файл
  1. base64hub
  2. Статьи о Base64
  3. Производительность data URI
Производительность

Производительность data URI: когда встраивать Base64 выгодно

Встраивая файл как Base64 data URI, вы экономите запрос, но платите байтами, кешированием, а иногда и временем рендеринга. Сжатие меняет расчёт, а для SVG меняет его по-своему.

Во что обходится data URI

Data URI помещает файл прямо в URL: data:image/png;base64,iVBORw0KGgo.... Его можно подставить везде, где ожидается URL, так что иконка может жить в атрибуте src в HTML или в background-image в CSS, и браузер никогда не сделает за ней отдельный запрос.

Компромиссы такие:

  • Размер. Base64 увеличивает данные до 4/3 от исходного объёма, примерно на 33% (точную формулу см. в статье о росте размера в Base64). Префикс data:image/png;base64, добавляет ещё 22 байта.
  • Кеширование. Встроенный файл кешируется только в составе документа, в котором он лежит. Логотип, встроенный в каждую HTML-страницу, скачивается заново с каждой страницей. Встроенный в CSS-файл, он кешируется вместе с этим CSS, и замена одной иконки сбрасывает кеш всей таблицы стилей.
  • Рендеринг. Браузер ничего не отрисует, пока не загрузится блокирующий рендеринг CSS. Таблица стилей с 200 КБ встроенных шрифтов или картинок откладывает первую отрисовку ровно на то время, которое нужно на загрузку этих байтов. В HTML встроенные данные отодвигают дальше по потоку байтов весь контент после них.
  • Никакой ленивой загрузки. loading="lazy" на <img> с data URI ничего не экономит: байты уже пришли вместе со страницей.

Взамен вы экономите запрос. В HTTP/1.1 это имело большое значение, потому что браузеры открывали к одному хосту лишь несколько соединений. В HTTP/2 и HTTP/3 множество запросов делят одно соединение, так что выигрыш меньше: в основном заголовки запроса и ответа плюс одна задержка на путь туда и обратно (RTT) для файлов, которые браузер обнаруживает поздно.

Замеры: что gzip и Brotli делают с Base64

Цифра 33% относится к несжатому тексту. Большая часть HTML и CSS отдаётся с gzip или Brotli, поэтому важен размер после сжатия. Мы измерили его сами.

Методика. Файлы-образцы из пакетов Debian 13: PNG-иконка 16×16 (тема иконок HighContrast), PNG 48×48 с RGBA (тема hicolor), синие обои Xfce в JPEG 2880×1800, уменьшенный из них в Pillow JPEG 800×500 с качеством 80, шрифт Roboto Slab Regular в WOFF2 и две SVG-иконки (символьная иконка Adwaita на 479 байт и завиток Debian из GNOME на 7,6 КБ). Каждый файл и его Base64 (только данные, без префикса data:) сжимались через zlib из Node.js 24: gzip с уровнем 9 и Brotli с качеством 11 (обычные настройки для предварительно сжатой статики). Размеры указаны в байтах.

ФайлИсходныйBase64Исходный, gzipBase64, gzipИсходный, BrotliBase64, Brotli
PNG 16×16281376285306285284
PNG 48×48455260724575460245514589
JPEG 800×500819510 9287425717371606908
JPEG 2880×1800197 011262 684182 684183 945180 114181 140
Шрифт WOFF252 53270 04452 53852 76552 53652 690
SVG-иконка479640291427247410
SVG-завиток761010 1483123439825823823

Что показывают цифры:

Уже сжатые двоичные файлы теряют почти весь лишний объём. Данные PNG, JPEG и WOFF2 уже сжаты и близки к случайным, поэтому повторное сжатие почти ничего не даёт (оба PNG под gzip даже немного выросли). Base64 от случайных байтов использует 64 символа по 8 бит, а кодирование Хаффмана возвращает это примерно к 6 битам на символ. Итог: сжатый Base64 занял от 96% до 107% от сжатого двоичного файла и от 84% до 109% от исходного. Сами JPEG здесь немного поддавались сжатию, поэтому их Base64-версии даже вышли чуть меньше, чем JPEG под gzip; разница слишком мала, чтобы на неё рассчитывать. На крошечном PNG 16×16 фиксированные 18 байт заголовка и хвоста gzip выглядят как большой процент, а более лёгкая обвязка Brotli это стёрла.

SVG становится хуже. В SVG-тексте куча повторов, и сжатие его обожает: завиток уменьшился под gzip с 7610 до 3123 байт. Base64 перекраивает каждую 3-байтную группу в 4 символа, значения которых зависят от выравнивания, так что повторяющаяся подстрока уже не выглядит одинаково дважды. Сжатый gzip-ом Base64 SVG был на 41–47% больше сжатого SVG, а с Brotli на 48–66%.

SVG в URL-кодировании против Base64

SVG вообще не нуждается в Base64: это и так текст. Data URI с URL-кодированием оставляет его читаемым:

.flag {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3 0v16h2V8h3l1 2h5V2h-4L9 0z'/%3E%3C/svg%3E");
}

Экранировать нужно как минимум %, # (он начал бы фрагмент URL), < и >, плюс ту кавычку, которой ограничен атрибут. Если заменить двойные кавычки внутри SVG на одинарные, их экранировать не придётся. Символы вне ASCII нужно закодировать в процентной форме как UTF-8.

Те же два SVG, измеренные тем же способом:

SVGКодированиеРазмерGzipBrotli
ИконкаBase64640427410
ИконкаМинимальное URL-кодирование493293253
ИконкаencodeURIComponent723326279
ЗавитокBase6410 14843983823
ЗавитокМинимальное URL-кодирование717930572549
ЗавитокencodeURIComponent11 59433192750

«Минимальное» означает: пробельные символы схлопнуты, двойные кавычки заменены на одинарные, экранированы только %, #, <, >. Такой вариант сжался примерно до размера исходного SVG, а завиток даже чуть меньше, потому что схлопывание пробелов убрало часть байтов. Полный encodeURIComponent больше в несжатом виде, но всё равно сжимается намного лучше Base64. Инструмент SVG в data URI выдаёт обе формы рядом.

Base64 для SVG по-прежнему оправдан, когда инструмент или API принимает только Base64 или когда в SVG есть символы, об экранировании которых вам не хочется думать.

Content Security Policy и data:

В Content Security Policy 'self' не включает URL со схемой data:. Каждая директива, которая должна их разрешать, обязана перечислить эту схему:

Content-Security-Policy: default-src 'self'; img-src 'self' data:; font-src 'self' data:

Разрешить data: в img-src и font-src считается обычной практикой с низким риском. С script-src, default-src или object-src всё иначе: если разрешить data: там, злоумышленник, способный внедрить разметку, сможет подсунуть скрипт без всякого внешнего хоста, и CSP во многом теряет смысл. Если политика мешает встроенным картинкам, расширяйте только img-src.

Кроме того, браузеры запрещают страницам переводить окно верхнего уровня на URL data: ссылкой, редиректом или скриптом. Поэтому data URI работает как встроенный ресурс, но не как цель ссылки.

Когда встраивание оправдано

Обычно оправдано:

  • Крошечные иконки и картинки интерфейса примерно до 1–2 КБ, особенно нужные для первой отрисовки. Накладные расходы на запрос составляют большую долю их размера, а сжатие стирает почти всю цену Base64.
  • HTML одним файлом. Офлайн-отчёты, выгруженная документация, страницы покрытия тестами и превью HTML-писем, которые должны работать при открытии с диска.
  • Критический CSS, который и так встроен в HTML, и маленькая фоновая картинка едет вместе с ним.
  • Сгенерированный контент, например график, отрисованный скриптом в PNG и вставленный в HTML-отчёт.

Обычно не оправдано:

  • Фотографии и всё, что больше нескольких килобайт. Им лучше быть отдельными файлами с долгим сроком кеширования.
  • Картинки, общие для многих страниц: они будут заново скачиваться внутри каждого HTML-ответа.
  • Веб-шрифты. Файл WOFF2 обычно весит десятки килобайт (измеренный выше файл весит 52 КБ), встраивание в CSS дольше держит таблицу стилей блокирующей, а отдельные файлы шрифтов можно кешировать и загружать через preload.
  • Всё, что ниже первого экрана. Отдельный файл с loading="lazy" ничего не стоит, пока не понадобится.

Электронная почта стоит особняком. Поддержка картинок data: в почтовых клиентах разная, так что проверяйте в тех клиентах, которыми пользуются ваши получатели. Надёжнее передать картинку в HTML-письме как встроенное MIME-вложение со ссылкой через cid:; это тоже Base64, только внутри структуры MIME. Об этом подробнее рассказано в статье Base64 в электронной почте.

Сделать data URI из картинки

Перетащите PNG, JPEG, WebP или SVG и получите data URI и его размер. Файл остаётся в вашем браузере.

Картинка в Base64

Вопросы и ответы

Картинки в Base64 медленнее обычных?

Сами по себе нет. Маленькие встроенные картинки избавляют от запроса, а большие раздувают HTML или CSS, не кешируются отдельно и могут задерживать рендеринг. До нескольких килобайт встраивание помогает, дальше вредит.

Убирает ли gzip 33% лишнего объёма Base64?

Для PNG, JPEG и WOFF2 почти полностью: в наших замерах сжатый gzip-ом Base64 оказывался в пределах нескольких процентов от двоичного файла. Для SVG и другого текста нет: сжатый Base64 SVG был на 41–47% больше сжатого SVG.

Для встроенного SVG лучше URL-кодирование или Base64?

URL-кодирование. Оно читаемое, редактируемое и сжимается примерно так же, как исходный SVG. Экранируйте как минимум #, %, < и >, а внутри SVG используйте одинарные кавычки, если атрибут заключён в двойные.

Почему Content Security Policy блокирует мои картинки в data URI?

CSP считает data: отдельной схемой, которую 'self' не покрывает. Добавьте data: в img-src (или в font-src для шрифтов). В script-src и default-src его не добавляйте.

Читайте также