Charger un fichier
  1. base64hub
  2. Apprendre Base64
  3. Performances des data URI
Performance

Performances des data URI : quand intégrer du Base64 est rentable

Intégrer un fichier sous forme de data URI Base64 économise une requête, mais coûte des octets, du cache et parfois du temps de rendu. La compression change le calcul, et pas de la même façon pour le SVG.

Ce que coûte une data URI

Une data URI place un fichier dans une URL : data:image/png;base64,iVBORw0KGgo.... Elle s’utilise partout où une URL est acceptée : une icône peut donc vivre dans l’attribut HTML src ou dans un background-image CSS, et le navigateur ne fait jamais de requête séparée pour elle.

Les contreparties :

  • La taille. Base64 porte le payload à 4/3 de l’original, soit environ 33 % de plus (formule exacte dans le surcoût de taille de Base64). Le préfixe data:image/png;base64, ajoute encore 22 octets.
  • Le cache. Le fichier intégré n’est mis en cache qu’en tant que partie du document qui le contient. Un logo intégré dans chaque page HTML est retéléchargé avec chaque page. Intégré dans un fichier CSS, il est mis en cache avec ce CSS, et modifier une seule icône invalide toute la feuille de style.
  • Le rendu. Le navigateur ne peut rien afficher tant que le CSS bloquant le rendu n’est pas arrivé. Une feuille de style qui transporte 200 Ko de polices ou d’images intégrées retarde le premier rendu du temps nécessaire pour télécharger ces octets. En HTML, les données intégrées repoussent plus loin dans le flux d’octets le contenu qui les suit.
  • Pas de chargement différé. loading="lazy" sur un <img> avec une data URI n’économise rien, puisque les octets sont déjà arrivés avec la page.

En échange, vous économisez une requête. En HTTP/1.1, c’était important, car les navigateurs n’ouvraient qu’une poignée de connexions par hôte. Avec HTTP/2 et HTTP/3, de nombreuses requêtes partagent une même connexion, et le gain est plus faible : surtout les en-têtes de requête et de réponse, plus un aller-retour de latence pour les fichiers que le navigateur découvre tard.

Mesures : ce que gzip et Brotli font au Base64

Le chiffre de 33 % vaut pour du texte non compressé. La plupart du HTML et du CSS est servie avec gzip ou Brotli : le chiffre qui compte est donc la taille compressée. Nous l’avons mesurée nous-mêmes.

Méthode. Des fichiers d’exemple tirés de paquets Debian 13 : une icône PNG 16×16 (thème d’icônes HighContrast), un PNG RGBA 48×48 (thème hicolor), le fond d’écran bleu Xfce en JPEG 2880×1800, un JPEG 800×500 redimensionné à partir de celui-ci avec Pillow en qualité 80, la police Roboto Slab Regular en WOFF2, et deux icônes SVG (une icône symbolique Adwaita de 479 octets et le tourbillon Debian de GNOME de 7,6 Ko). Chaque fichier et son encodage Base64 (le payload seul, sans le préfixe data:) ont été compressés avec le module zlib de Node.js 24 : gzip au niveau 9 et Brotli en qualité 11, les réglages habituels pour des ressources statiques précompressées. Les tailles sont en octets.

FichierBrutBase64Brut gzipBase64 gzipBrut BrotliBase64 Brotli
PNG 16×16281376285306285284
PNG 48×484,5526,0724,5754,6024,5514,589
JPEG 800×5008,19510,9287,4257,1737,1606,908
JPEG 2880×1800197,011262,684182,684183,945180,114181,140
Police WOFF252,53270,04452,53852,76552,53652,690
Icône SVG479640291427247410
Tourbillon SVG7,61010,1483,1234,3982,5823,823

Ce que montrent les chiffres :

Les binaires déjà compressés perdent presque tout le surcoût. Les données PNG, JPEG et WOFF2 sont déjà compressées et proches de l’aléatoire : les recompresser apporte peu (les deux PNG sont même devenus légèrement plus gros avec gzip). Le Base64 d’octets aléatoires utilise 64 symboles de 8 bits chacun, et un codeur de Huffman ramène cela à environ 6 bits par symbole. Le Base64 compressé s’est situé entre 96 % et 107 % du binaire compressé, et entre 84 % et 109 % du fichier brut. Les JPEG utilisés ici étaient eux-mêmes un peu compressibles, ce qui explique que leurs versions Base64 soient même sorties légèrement plus petites que le JPEG compressé en gzip ; l’écart est trop faible pour en tenir compte. Le minuscule PNG 16×16 fait apparaître les 18 octets fixes d’en-tête et de fin de gzip comme un fort pourcentage, et l’enveloppe plus légère de Brotli les a effacés.

Le SVG s’en sort moins bien. Le SVG est du texte très répétitif, et la compression adore ça : le tourbillon est passé de 7 610 à 3 123 octets avec gzip. Base64 répartit chaque groupe de 3 octets sur 4 caractères dont les valeurs dépendent de l’alignement : une sous-chaîne répétée n’a donc plus deux fois la même apparence. Le SVG en Base64 compressé en gzip était 41 à 47 % plus gros que le SVG compressé en gzip, et 48 à 66 % plus gros avec Brotli.

SVG encodé en URL ou en Base64

Le SVG n’a pas du tout besoin de Base64, puisque c’est déjà du texte. Une data URI encodée en URL le garde lisible :

.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");
}

Le minimum à échapper : %, # (il ouvrirait un fragment d’URL), < et >, plus le type de guillemet qui délimite l’attribut. Remplacer les guillemets doubles du SVG par des guillemets simples évite de les échapper. Les caractères non ASCII doivent être encodés en pourcent en UTF-8.

Les deux mêmes SVG, mesurés de la même façon :

SVGEncodageTailleGzipBrotli
IcôneBase64640427410
IcôneEncodage URL minimal493293253
IcôneencodeURIComponent723326279
TourbillonBase6410,1484,3983,823
TourbillonEncodage URL minimal7,1793,0572,549
TourbillonencodeURIComponent11,5943,3192,750

« Minimal » signifie : espaces réduits, guillemets doubles remplacés par des simples, et seuls %, #, <, > échappés. Le résultat s’est compressé à peu près à la taille du SVG d’origine ; le tourbillon est même un peu plus petit, car la réduction des espaces a retiré des octets. Un encodeURIComponent complet est plus gros sans compression, mais se compresse encore bien mieux que Base64. L’outil SVG en data URI produit les deux formes côte à côte.

Base64 garde un intérêt pour le SVG quand un outil ou une API n’accepte que du Base64, ou quand le SVG contient des caractères dont vous préférez ne pas vous soucier de l’échappement.

Content Security Policy et data:

Avec une Content Security Policy, 'self' n’inclut pas les URL data:. Chaque directive qui doit les autoriser doit mentionner le schéma :

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

Autoriser data: dans img-src et font-src est courant et peu risqué. L’autoriser dans script-src, default-src ou object-src est une autre affaire : un attaquant capable d’injecter du balisage peut alors fournir un script sans avoir besoin d’aucun hôte externe, ce qui annule une bonne partie de l’intérêt de la CSP. Si des images intégrées se heurtent à votre politique, élargissez seulement img-src.

Les navigateurs empêchent aussi les pages de faire naviguer la fenêtre principale vers une URL data:, que ce soit par un lien, une redirection ou un script : une data URI peut être intégrée dans une page, mais pas ouverte comme lien de premier niveau.

Quand l’intégration vaut le coup

En général, ça vaut le coup :

  • Les petites icônes et images d’interface de moins de 1 à 2 Ko environ, surtout celles nécessaires au premier rendu. Le coût de la requête représente une grande part de leur taille, et la compression efface l’essentiel du coût de Base64.
  • Le HTML en un seul fichier. Rapports hors ligne, documentation exportée, pages de couverture de tests et aperçus d’e-mails HTML qui doivent fonctionner une fois ouverts depuis le disque.
  • Le CSS critique déjà intégré dans le HTML, avec lequel une petite image de fond voyage.
  • Le contenu généré, comme un graphique rendu en PNG par un script et intégré dans un rapport HTML.

En général, ça ne vaut pas le coup :

  • Les photos et tout ce qui dépasse quelques kilo-octets. Mieux vaut des fichiers séparés avec une longue durée de cache.
  • Les images partagées entre de nombreuses pages, qui seraient retéléchargées dans chaque réponse HTML.
  • Les polices web. Un fichier WOFF2 fait généralement plusieurs dizaines de kilo-octets (celui mesuré plus haut fait 52 Ko) ; l’intégrer au CSS prolonge le blocage du rendu par la feuille de style, alors que des fichiers de police séparés peuvent être mis en cache et préchargés.
  • Tout ce qui se trouve sous la ligne de flottaison. Un fichier séparé avec loading="lazy" ne coûte rien tant qu’il n’est pas nécessaire.

L’e-mail est un cas à part. La prise en charge des images data: varie selon les clients de messagerie : testez dans ceux qu’utilisent réellement vos destinataires. La méthode fiable pour intégrer une image dans un e-mail HTML est une pièce jointe MIME inline référencée avec cid: ; c’est toujours du Base64, mais à l’intérieur de la structure MIME. Voir Base64 dans l’e-mail.

Créer une data URI à partir d'une image

Déposez un PNG, un JPEG, un WebP ou un SVG et obtenez la data URI et sa taille. Le fichier reste dans votre navigateur.

Image en Base64

FAQ

Les images en Base64 sont-elles plus lentes que les images normales ?

Pas en soi. Les petites images intégrées évitent une requête, tandis que les grandes alourdissent le HTML ou le CSS, ne peuvent pas être mises en cache séparément et peuvent retarder le rendu. L’intégration aide en dessous de quelques kilo-octets et pénalise au-delà.

Gzip supprime-t-il le surcoût de 33 % de Base64 ?

Pour PNG, JPEG et WOFF2, presque entièrement : dans nos mesures, le Base64 compressé en gzip arrivait à quelques pour cent du fichier binaire. Pour le SVG et les autres textes, non : le SVG en Base64 compressé en gzip était 41 à 47 % plus gros que le SVG compressé en gzip.

Pour du SVG intégré, vaut-il mieux l'encodage URL ou Base64 ?

L’encodage URL. Il reste lisible, modifiable, et se compresse à peu près aussi bien que le SVG d’origine. Échappez au moins #, %, < et >, et utilisez des guillemets simples dans le SVG si l’attribut est entre guillemets doubles.

Pourquoi mes images en data URI sont-elles bloquées par la Content Security Policy ?

La CSP traite data: comme un schéma distinct que 'self' ne couvre pas. Ajoutez data: à img-src (ou à font-src pour les polices). Ne l’ajoutez ni à script-src ni à default-src.

Continuer la lecture