O que um data URI custa
Um data URI coloca um arquivo dentro de uma URL: data:image/png;base64,iVBORw0KGgo.... Ele pode ser usado em qualquer lugar onde cabe uma URL, então um ícone pode morar dentro do atributo src do HTML ou de um background-image do CSS, e o navegador nunca faz uma requisição separada para ele.
Os custos são:
- Tamanho. O Base64 deixa o conteúdo com 4/3 do original, cerca de 33% maior (fórmula exata em acréscimo de tamanho do Base64). O prefixo
data:image/png;base64,soma mais 22 bytes. - Cache. O arquivo embutido só é cacheado como parte do documento que o contém. Um logo embutido em todas as páginas HTML é baixado de novo com cada página. Embutido num arquivo CSS, ele é cacheado com esse CSS, e trocar um ícone invalida a folha de estilos inteira.
- Renderização. O navegador não pinta nada até o CSS que bloqueia a renderização chegar. Uma folha de estilos com 200 KB de fontes ou imagens embutidas atrasa a primeira renderização pelo tempo que esses bytes levam para baixar. No HTML, os dados embutidos empurram o conteúdo seguinte mais para baixo no fluxo de bytes.
- Sem lazy loading.
loading="lazy"num<img>com data URI não economiza nada, porque os bytes já chegaram com a página.
Em compensação, você economiza uma requisição. No HTTP/1.1 isso era muita coisa, já que os navegadores abriam só algumas conexões por host. Com HTTP/2 e HTTP/3, muitas requisições compartilham uma conexão, então a economia é menor: basicamente os headers da requisição e da resposta, mais uma ida e volta de latência para arquivos que o navegador descobre tarde.
Medido: o que gzip e Brotli fazem com o Base64
Os 33% valem para texto sem compressão. A maior parte do HTML e do CSS é servida com gzip ou Brotli, então o número que importa é o tamanho compactado. Nós medimos por conta própria.
Método. Arquivos de exemplo de pacotes do Debian 13: um ícone PNG 16×16 (tema de ícones HighContrast), um PNG RGBA 48×48 (tema hicolor), o papel de parede azul do Xfce em JPEG 2880×1800, um JPEG 800×500 redimensionado a partir dele com Pillow na qualidade 80, a fonte Roboto Slab Regular em WOFF2 e dois ícones SVG (um ícone simbólico do Adwaita de 479 bytes e o redemoinho do Debian do GNOME, de 7,6 KB). Cada arquivo e sua codificação Base64 (só o conteúdo, sem o prefixo data:) foram compactados com o zlib do Node.js 24: gzip no nível 9 e Brotli na qualidade 11, as configurações típicas para assets estáticos pré-compactados. Tamanhos em bytes.
| Arquivo | Bruto | Base64 | Bruto gzip | Base64 gzip | Bruto Brotli | Base64 Brotli |
|---|---|---|---|---|---|---|
| PNG 16×16 | 281 | 376 | 285 | 306 | 285 | 284 |
| PNG 48×48 | 4,552 | 6,072 | 4,575 | 4,602 | 4,551 | 4,589 |
| JPEG 800×500 | 8,195 | 10,928 | 7,425 | 7,173 | 7,160 | 6,908 |
| JPEG 2880×1800 | 197,011 | 262,684 | 182,684 | 183,945 | 180,114 | 181,140 |
| Fonte WOFF2 | 52,532 | 70,044 | 52,538 | 52,765 | 52,536 | 52,690 |
| Ícone SVG | 479 | 640 | 291 | 427 | 247 | 410 |
| Redemoinho SVG | 7,610 | 10,148 | 3,123 | 4,398 | 2,582 | 3,823 |
O que os números mostram:
Binários já compactados perdem quase todo o acréscimo. Dados PNG, JPEG e WOFF2 já são compactados e quase aleatórios, então compactar de novo ganha pouco (os dois PNGs ficaram um pouco maiores com gzip). O Base64 de bytes aleatórios usa 64 símbolos de 8 bits cada, e um codificador Huffman traz isso de volta para cerca de 6 bits por símbolo. O Base64 compactado ficou entre 96% e 107% do binário compactado, e entre 84% e 109% do arquivo bruto. Os JPEGs daqui ainda eram um pouco compactáveis, por isso as versões em Base64 até saíram um pouco menores que o JPEG com gzip; a diferença é pequena demais para entrar no planejamento. No minúsculo PNG 16×16, os 18 bytes fixos de cabeçalho e rodapé do gzip pesam bastante em porcentagem, e o enquadramento mais leve do Brotli os eliminou.
O SVG piora. SVG é texto com muita repetição, e a compressão adora isso: o redemoinho caiu de 7.610 para 3.123 bytes com gzip. O Base64 embaralha cada grupo de 3 bytes em 4 caracteres cujos valores dependem do alinhamento, então uma substring repetida deixa de parecer igual nas duas vezes. O SVG em Base64 com gzip ficou 41–47% maior que o SVG com gzip, e com Brotli, 48–66% maior.
SVG com codificação de URL vs Base64
O SVG nem precisa de Base64, porque já é texto. Um data URI com codificação de URL o mantém legível:
.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");
}
O mínimo a escapar é %, # (ele iniciaria um fragmento de URL), < e >, mais o caractere de aspas que delimita o atributo. Trocar as aspas duplas dentro do SVG por aspas simples evita ter que escapá-las. Caracteres não ASCII precisam de codificação percentual como UTF-8.
Os mesmos dois SVGs, medidos do mesmo jeito:
| SVG | Codificação | Tamanho | Gzip | Brotli |
|---|---|---|---|---|
| Ícone | Base64 | 640 | 427 | 410 |
| Ícone | Codificação de URL mínima | 493 | 293 | 253 |
| Ícone | encodeURIComponent | 723 | 326 | 279 |
| Redemoinho | Base64 | 10,148 | 4,398 | 3,823 |
| Redemoinho | Codificação de URL mínima | 7,179 | 3,057 | 2,549 |
| Redemoinho | encodeURIComponent | 11,594 | 3,319 | 2,750 |
“Mínima” significa espaços em branco reduzidos, aspas duplas trocadas por simples e apenas %, #, <, > escapados. Ela compactou para mais ou menos o mesmo tamanho do SVG original; o redemoinho ficou até um pouco menor, porque reduzir os espaços removeu bytes. O encodeURIComponent completo é maior sem compressão, mas ainda compacta muito melhor que o Base64. A ferramenta SVG para data URI gera as duas formas lado a lado.
O Base64 ainda faz sentido para SVG quando uma ferramenta ou API só aceita Base64, ou quando o SVG contém caracteres cujo escape você prefere não ter que pensar.
Content Security Policy e data:
Sob um Content Security Policy, 'self' não inclui URLs data:. Cada diretiva que deve permiti-las precisa listar o esquema:
Content-Security-Policy: default-src 'self'; img-src 'self' data:; font-src 'self' data:
Permitir data: em img-src e font-src é comum e de baixo risco. Permitir em script-src, default-src ou object-src é outra história: isso deixa um atacante que consegue injetar marcação fornecer um script sem precisar de nenhum host externo, o que derruba boa parte do sentido do CSP. Se as imagens embutidas esbarram na sua política, amplie só o img-src.
Os navegadores também impedem que páginas naveguem a janela principal para uma URL data:, seja por link, redirecionamento ou script, então um data URI pode ser embutido numa página, mas não pode ser aberto como link de nível superior.
Quando embutir vale a pena
Geralmente vale:
- Ícones minúsculos e imagens de interface abaixo de 1–2 KB, mais ou menos, principalmente as necessárias na primeira renderização. O custo da requisição é uma fração grande do tamanho delas, e a compressão apaga quase todo o custo do Base64.
- HTML em arquivo único. Relatórios offline, documentação exportada, páginas de cobertura de testes e prévias de e-mail em HTML que precisam funcionar quando abertas do disco.
- CSS crítico que já vai embutido no HTML, onde uma pequena imagem de fundo viaja junto.
- Conteúdo gerado, como um gráfico renderizado em PNG por um script e embutido num relatório HTML.
Geralmente não vale:
- Fotos e qualquer coisa acima de alguns kilobytes. Elas funcionam melhor como arquivos separados com cache de longa duração.
- Imagens compartilhadas por muitas páginas, que seriam baixadas de novo dentro de cada resposta HTML.
- Fontes web. Um arquivo WOFF2 costuma ter dezenas de kilobytes (o medido acima tem 52 KB); embuti-lo no CSS faz a folha de estilos bloquear a renderização por mais tempo, e arquivos de fonte separados podem ser cacheados e pré-carregados.
- Qualquer coisa abaixo da dobra. Um arquivo separado com
loading="lazy"não custa nada até ser necessário.
E-mail é um caso à parte. O suporte a imagens data: varia entre clientes de e-mail, então teste nos clientes que seus destinatários realmente usam. O jeito confiável de embutir uma imagem num e-mail HTML é um anexo MIME inline referenciado com cid:, que continua sendo Base64, só que dentro da estrutura MIME. Veja Base64 no e-mail.
Arraste um PNG, JPEG, WebP ou SVG e receba o data URI e o tamanho dele. O arquivo fica no seu navegador.
Perguntas frequentes
Imagens em Base64 são mais lentas que imagens normais?
Não por natureza. Imagens pequenas embutidas evitam uma requisição, enquanto as grandes incham o HTML ou o CSS, não podem ser cacheadas separadamente e podem atrasar a renderização. Embutir ajuda abaixo de alguns kilobytes e atrapalha acima disso.
O gzip elimina o acréscimo de 33% do Base64?
Para PNG, JPEG e WOFF2, quase totalmente: nas nossas medições o Base64 compactado com gzip ficou a poucos por cento do arquivo binário. Para SVG e outros textos, não: o SVG em Base64 com gzip ficou 41–47% maior que o SVG com gzip.
Para SVG embutido, o que é melhor: codificação de URL ou Base64?
A codificação de URL. Ela é legível, editável e compacta mais ou menos tão bem quanto o SVG original. Escape pelo menos #, %, < e >, e use aspas simples dentro do SVG se o atributo usar aspas duplas.
Por que minhas imagens em data URI são bloqueadas pelo Content Security Policy?
O CSP trata data: como um esquema separado que 'self' não cobre. Adicione data: ao img-src (ou ao font-src para fontes). Não o adicione ao script-src nem ao default-src.