[GUIDE] Comprendre les URI de données : data:image/png;base64,... expliqué
Un guide pratique du schéma d'URI data: — ce que signifie réellement chaque partie de data:image/png;base64,iVBORw0KGgo..., quand l'intégration est le bon choix, et quand elle se retourne contre vous.
// CE QU'EST RÉELLEMENT UNE URI data:
Une URI data: est un schéma d'URI — comme https: ou file: — qui intègre la ressource elle-même directement dans la chaîne d'URI au lieu de pointer vers un emplacement distant. Définie par la RFC 2397, elle vous permet de traiter un petit bloc de données binaires ou textuelles comme s'il s'agissait d'une URL.
La forme la plus courante que vous rencontrerez concerne les images : data:image/png;base64,iVBORw0KGgoAAAANSUhEUg.... Le navigateur voit cette URL, lit le type MIME, décode la charge utile Base64 en octets et affiche l'image — sans jamais effectuer de requête HTTP.
// LA SYNTAXE, MORCEAU PAR MORCEAU
La syntaxe complète est data:[<mediatype>][;base64],<data>. Trois parties optionnelles et un séparateur obligatoire (la virgule). Décortiquons un exemple réel :
Prenez data:image/png;base64,iVBORw0KGgoAAAANSUhEUg... et lisez-le de gauche à droite :
• data: — le schéma. Indique à l'analyseur : « la ressource est dans l'URL elle-même, aucune requête nécessaire ».
• image/png — le type MIME. Indique au moteur de rendu comment interpréter la charge utile. Par défaut text/plain;charset=US-ASCII s'il est omis.
• ;base64 — drapeau d'encodage. Indique que la charge utile est en Base64 (RFC 4648). Sans lui, la charge utile est interprétée comme du texte encodé URL.
• , — le séparateur obligatoire entre les métadonnées et la charge utile.
• iVBORw0KGgo... — les octets réels de l'image encodés en Base64.
// Anatomy diagram
// data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...
// │ │ │ │
// │ │ │ └── Base64 payload (the actual image bytes)
// │ │ └────────── encoding flag (always 'base64' for binary)
// │ └──────────────────── MIME type (optional, defaults to text/plain)
// └───────────────────────── scheme literal (always 'data:')
// POURQUOI LES URI data: EXISTENT
Trois raisons poussent à utiliser des URI de données plutôt que des URL d'images classiques :
1. Éliminer une requête HTTP. Chaque <img src="..."> sur une page coûte un aller-retour réseau. Pour une petite icône ou un sprite, cet aller-retour peut prendre plus de temps que le transfert des octets eux-mêmes. L'intégration de l'image en tant qu'URI de données la regroupe avec le HTML/CSS, supprimant entièrement la requête. Cela importait beaucoup à l'ère HTTP/1.1 ; avec le multiplexage HTTP/2, cela importe moins, mais pour les icônes critiques au-dessus de la ligne de flottaison, cela aide encore.
2. Rendre l'actif autonome. Un e-mail HTML doit s'afficher dans des clients qui bloquent par défaut les images distantes (Gmail, Outlook). Un rapport statique que vous envoyez par e-mail à un client doit afficher le logo de l'entreprise même hors ligne. Un bookmarklet a besoin d'une icône qui survive au copier-coller. Un extrait de documentation a besoin d'une capture d'écran qui voyage avec le markdown. Dans tous ces cas, l'image doit vivre à l'intérieur du document — et une URI de données est la façon de procéder.
3. Génération programmatique. Le code génère une image à l'exécution — un code QR, un graphique, un pavé de signature — et vous devez l'afficher sans d'abord la téléverser sur un serveur et récupérer une URL. canvas.toDataURL('image/png') vous donne directement une URI de données ; l'assigner à img.src est le flux de travail le plus simple possible.
// TOUS LES TYPES MIME QUE VOUS RENCONTREREZ EN PRATIQUE
L'emplacement du type MIME accepte n'importe quel type de média, mais en pratique vous en verrez une petite poignée pour les images :
• image/png — le plus courant. Sans perte, prend en charge la transparence. Le Base64 commence par iVBORw0KGgo.
• image/jpeg — photos et captures d'écran. Avec perte. Le Base64 commence par /9j/.
• image/gif — images héritées et animées. Le Base64 commence par R0lGOD.
• image/webp — moderne, fichiers plus petits. Le Base64 commence par UklGR.
• image/svg+xml — graphiques vectoriels. Le SVG est du texte, vous pouvez donc soit l'encoder en Base64 (avec ;base64) soit l'encoder en URL (sans). Le SVG encodé URL est généralement plus petit.
• image/x-icon ou image/vnd.microsoft.icon — favicons. Le Base64 commence par AAABAA.
<!-- All five types in a single HTML document -->
<img src="data:image/png;base64,iVBORw0KGgo..." alt="PNG inline">
<img src="data:image/jpeg;base64,/9j/4AAQSkZJRg..." alt="JPEG inline">
<img src="data:image/gif;base64,R0lGODlhAQABAA..." alt="GIF inline">
<img src="data:image/webp;base64,UklGRiIAAABXRU..." alt="WebP inline">
<img src="data:image/svg+xml;base64,PHN2ZyB4bWxu..." alt="SVG inline">
// UTILISER LES URI data: EN HTML, CSS ET JAVASCRIPT
Partout où une URL est acceptée, une URI de données fonctionne de la même manière.
<!-- HTML <img> tag -->
<img src="data:image/png;base64,iVBORw0KGgo..." width="32" height="32" alt="icon">
<!-- HTML <link> for favicon -->
<link rel="icon" href="data:image/x-icon;base64,AAABAAEAEBA...">
/* CSS background-image */
.button-icon {
background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxu...');
background-size: 16px 16px;
}
/* CSS @font-face — yes, fonts work too */
@font-face {
font-family: 'InlineFont';
src: url('data:font/woff2;base64,d09GMgAB...') format('woff2');
}
// JavaScript — assign directly to img.src
const img = new Image();
img.src = 'data:image/png;base64,iVBORw0KGgo...';
document.body.appendChild(img);
// JavaScript — fetch a data URI like any other URL
const response = await fetch('data:application/json;base64,eyJrZXkiOiJ2YWx1ZSJ9');
const data = await response.json(); // { key: 'value' }
// CANVAS, FILE ET BLOB → URI data:
Trois API navigateur vous fournissent une URI de données sans que vous ayez à effectuer le Base64 vous-même :
// 1. Canvas → data URI (instant)
const canvas = document.querySelector('canvas');
const pngDataURI = canvas.toDataURL('image/png');
const jpegDataURI = canvas.toDataURL('image/jpeg', 0.85); // quality 0–1
// pngDataURI === 'data:image/png;base64,iVBORw0KGgo...'
// 2. <input type="file"> → data URI via FileReader
const file = document.querySelector('input[type=file]').files[0];
const reader = new FileReader();
reader.onload = () => {
// reader.result is the data URI
document.querySelector('img').src = reader.result;
};
reader.readAsDataURL(file);
// 3. Blob → data URI (manual, but rarely needed — use URL.createObjectURL instead)
const blob = new Blob([bytes], { type: 'image/png' });
const reader2 = new FileReader();
reader2.onload = () => console.log(reader2.result);
reader2.readAsDataURL(blob);
// Or, for in-page display, prefer:
const objectURL = URL.createObjectURL(blob); // 'blob:https://...' — much shorter, no Base64
// LA PÉNALITÉ DE TAILLE DE 33 % (ET QUAND ELLE COMPTE)
Le Base64 transforme 3 octets binaires en 4 caractères ASCII, donc la charge utile encodée fait exactement 4⌈n/3⌉ octets — environ 33 % de plus que l'original. Un PNG de 12 Ko devient une URI de données d'environ 16 Ko, plus quelques octets de surcoût pour le préfixe data:image/png;base64,.
Quand cela compte-t-il ?
• Petits actifs (moins de 4 Ko) : le gonflement de 33 % est éclipsé par l'aller-retour HTTP économisé. Intégrez librement.
• Actifs moyens (4–50 Ko) : au cas par cas. Avec HTTP/2 + mise en cache, une requête séparée est généralement plus rapide que de retélécharger la version intégrée à chaque chargement de page.
• Grands actifs (50 Ko et plus) : presque jamais à intégrer. L'URI de données gonfle chaque page HTML mise en cache, chaque e-mail, chaque charge utile JSON qui la contient. Utilisez une URL séparée.
Il n'y a pas de mise en cache pour les URI de données — le navigateur ne peut pas mettre en cache une image dont les octets sont incorporés dans le HTML. Si la même image apparaît sur plusieurs pages, chaque page paie le coût total. Une <img src="/logo.png"> classique avec des en-têtes de cache HTTP est téléchargée une fois et réutilisée partout.
// LIMITES DES NAVIGATEURS QUE VOUS ATTEINDREZ
Les navigateurs n'imposent pas de limite stricte unique sur les URI de données, mais des plafonds réels existent :
• Chrome / Edge / Firefox : les URI de données dans <img src> fonctionnent jusqu'à plusieurs mégaoctets. Au-delà d'environ 32 Mo, vous rencontrerez une pression mémoire par onglet.
• Safari : historiquement plafonné les URI de données dans <a href> (liens de téléchargement) à environ 2 Mo. Pour <img>, des tailles plus grandes fonctionnent mais le rendu ralentit.
• Internet Explorer 8 : 32 Ko maximum pour les URI de données dans CSS et HTML. (IE9+ a supprimé la limite mais cela n'a jamais été aussi rapide que les navigateurs modernes.)
• Ligne de requête HTTP : les URI de données dans <form action> ou les chaînes de requête sont limitées par la longueur d'URL acceptée par le navigateur (~2 Ko pour IE, 8 Ko et plus pour les autres).
Si vous vous approchez de l'une de ces limites, l'actif est trop volumineux pour être intégré. Passez à une URL classique ou à URL.createObjectURL(blob), qui vous donne une URL blob: courte adossée à un blob que vous pouvez conserver en mémoire.
// PRÉOCCUPATIONS DE SÉCURITÉ
Les URI de données sont sans réseau, ce qui est pratique — mais elles contournent plusieurs mécanismes de sécurité web qui supposent que le contenu provient d'un serveur.
Politique de même origine. Une URI data: est techniquement considérée comme une origine opaque (dans Chrome/Firefox modernes), donc le JavaScript chargé depuis une URI de données ne peut pas accéder aux cookies, au localStorage ou au DOM du document parent de manière non restreinte. C'est bien pour le bac à sable de contenu non fiable, mais cela signifie également que le suivi, l'analytique et les protections CSRF qui reposent sur des vérifications d'origine peuvent mal se comporter.
XSS via data:text/html. Une URL data:text/html,<script>...</script> exécutée par un utilisateur devient une page entièrement privilégiée. Les navigateurs modernes bloquent la navigation de niveau supérieur vers data:text/html pour cette raison (Firefox depuis 2018, Chrome depuis 2020). Ne faites pas passer les URL fournies par l'utilisateur à travers une redirection qui autorise le schéma data:.
Politique de sécurité du contenu (CSP). Par défaut, les CSP strictes bloquent les URI de données dans img-src, style-src, etc. Pour les autoriser, vous devez explicitement inclure data: dans la directive (par exemple, img-src 'self' data:). Soyez intentionnel — une fois que vous autorisez les URI de données, vous acceptez également toute image (ou police) que la page choisit d'intégrer.
Journalisation et IIP. Une URI de données fait partie de l'URL. Tout outil de journalisation ou d'analytique qui capture les URL (journaux d'accès serveur, historique du navigateur, traceurs d'erreurs) capturera la charge utile entière. Si votre URI de données contient l'avatar, la photo ou la capture d'écran d'un utilisateur, elle se retrouve dans vos journaux. Ne mettez pas d'IIP dans des URL que vous ne contrôlez pas.
// URI data: VS URL blob: — LAQUELLE UTILISER ?
Les deux vous permettent d'afficher des données binaires en mémoire sans les téléverser. Le choix dépend de l'endroit où les données doivent vivre.
Utilisez l'URI data: lorsque les données doivent être portables — intégrées dans du HTML, envoyées en JSON, sauvegardées dans une base de données, copiées-collées dans un e-mail. La donnée est l'URL.
Utilisez l'URL blob: lorsque les données sont locales et de courte durée — un aperçu dans une application monopage, un déclencheur de téléchargement, une image qui disparaît à la fermeture de la page. URL.createObjectURL(blob) vous donne un identifiant court (blob:https://example.com/12345-abcde) que le navigateur fait correspondre au blob en mémoire. Bien plus petit qu'une URI de données, et vous pouvez le révoquer avec URL.revokeObjectURL(url) une fois terminé.
Règle générale : si l'URL doit quitter la page actuelle, utilisez data:. Si elle reste à l'intérieur de la page, utilisez blob:.
// PROBLÈMES DE DÉBOGAGE COURANTS
Chaîne tronquée depuis les DevTools. Lorsque vous copiez une longue URI de données depuis l'onglet Réseau ou le panneau Éléments, les navigateurs tronquent souvent la valeur avec des points de suspension. Le texte affiché n'est pas l'URL complète. Pour obtenir la chaîne complète, ouvrez directement le fichier source (CSS, HTML, JSON), ou utilisez document.querySelector('img').src dans la console — cela retourne l'URL complète.
Mauvais type MIME. Une charge utile PNG étiquetée data:image/jpeg;base64,... sera quand même décodée au niveau octet — le Base64 ne se soucie pas du MIME — mais certains visualiseurs rejettent l'incompatibilité. Le format réel est déterminé par les octets magiques (iVBORw0KGgo pour PNG), donc en cas de doute, collez la charge utile dans notre décodeur Base64 vers image ; il détecte automatiquement à partir des octets.
Virgule manquante. Le format est data:image/png;base64,iVBORw..., pas data:image/png;base64;iVBORw.... Un point-virgule ou un séparateur manquant rend l'URL entièrement invalide.
Base64 sécurisé pour URL dans une URI data:. Les URI data: standard utilisent l'alphabet Base64 standard (avec + et /). Si votre charge utile a été encodée avec la variante sécurisée pour URL (RFC 4648 §5, avec - et _), vous devez la reconvertir avant l'intégration — voir notre guide Base64 sécurisé pour URL vs standard.
Espaces blancs à l'intérieur de la charge utile. Certains formateurs enveloppent les longues chaînes Base64 sur 76 colonnes avec des sauts de ligne. La plupart des navigateurs tolèrent cela dans <img src>, mais quelques-uns (en particulier les anciens WebViews) ne le font pas. Supprimez les espaces blancs avant l'intégration : str.replace(/\s+/g, '').
// QUAND NE PAS UTILISER LES URI data:
• Au-dessus de 50 Ko. Le coût d'invalidation du cache + téléchargement l'emporte sur la requête économisée.
• Images répétées. Tout ce qui est utilisé sur plus d'une page devrait être une URL classique avec des en-têtes de cache.
• Analytique publique. Les pixels de suivi, balises et attributions basées sur des pixels ne fonctionneront pas car il n'y a pas de requête HTTP à journaliser.
• Contenu téléversé par l'utilisateur affiché dans une SPA. Utilisez URL.createObjectURL(blob) à la place — cela évite entièrement le travail Base64 et est environ 4× plus petit en mémoire.
• Dans un environnement CSP strict qui n'autorise pas data:. N'ajoutez pas data: à votre CSP juste pour utiliser une URI de données ; demandez d'abord si vous en avez vraiment besoin.
// AIDE-MÉMOIRE EN 30 SECONDES
Format : data:[mime-type][;base64],[payload]
Forme la plus courante : data:image/png;base64,iVBORw0KGgo...
La décoder vers une image : collez dans notre convertisseur Base64 vers image.
Encoder une image en URI de données : utilisez notre convertisseur image vers Base64 — il produit l'URI data: complète prête à coller.
Budget d'intégration : < 4 Ko toujours intégrer, 4–50 Ko cela dépend, > 50 Ko utilisez une URL classique.
API navigateur : canvas.toDataURL(), FileReader.readAsDataURL(), ou construisez simplement la chaîne vous-même : 'data:image/png;base64,' + btoa(binaryString).
Lectures associées : Intégration d'images Base64 · Aperçu d'images Base64 dans le navigateur · Base64 vs Base64URL vs Base32