[GUÍA] 10 min read

[GUÍA] Entendiendo los URI de datos: data:image/png;base64,... explicado

Una guía práctica sobre el esquema de URI data: — qué significa realmente cada parte de data:image/png;base64,iVBORw0KGgo..., cuándo incrustar en línea es la decisión correcta y cuándo se vuelve en tu contra.

April 2026 | fundamentals

// QUÉ ES REALMENTE UN URI data:

Un URI data: es un esquema de URI — como https: o file: — que incrusta el recurso en sí directamente dentro de la cadena del URI en lugar de apuntar a una ubicación remota. Definido por el RFC 2397, te permite tratar un pequeño bloque de datos binarios o de texto como si fuera una URL.

La forma más común que verás en producción es para imágenes: data:image/png;base64,iVBORw0KGgoAAAANSUhEUg.... El navegador ve esta URL, lee el tipo MIME, decodifica la carga útil en Base64 de vuelta a bytes y renderiza la imagen — sin hacer nunca una solicitud HTTP.

// LA SINTAXIS, PIEZA POR PIEZA

La sintaxis completa es data:[<mediatype>][;base64],<data>. Tres partes opcionales y un separador obligatorio (la coma). Vamos a desglosar un ejemplo real:

Toma data:image/png;base64,iVBORw0KGgoAAAANSUhEUg... y léelo de izquierda a derecha:
data: — el esquema. Le dice al analizador "el recurso está en la propia URL, no se necesita ninguna solicitud".
image/png — el tipo MIME. Le indica al renderizador cómo interpretar la carga útil. Por defecto es text/plain;charset=US-ASCII si se omite.
;base64 — bandera de codificación. Indica que la carga útil está en Base64 (RFC 4648). Sin ella, la carga útil se interpreta como texto codificado en URL.
, — el separador obligatorio entre los metadatos y la carga útil.
iVBORw0KGgo... — los bytes reales de la imagen codificados 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:')

// POR QUÉ EXISTEN LOS URI data:

Hay tres razones por las que la gente recurre a los URI de datos en lugar de URLs de imágenes regulares:

1. Eliminar una solicitud HTTP. Cada <img src="..."> en una página cuesta un viaje de ida y vuelta a la red. Para un ícono o sprite diminuto, ese viaje puede tardar más que transferir los propios bytes. Incrustar la imagen como un URI de datos la empaqueta junto con el HTML/CSS, eliminando la solicitud por completo. Esto importaba mucho en la era de HTTP/1.1; con la multiplexación de HTTP/2 importa menos, pero para íconos críticos por encima del pliegue todavía ayuda.

2. Hacer que el recurso sea autocontenido. Un correo HTML necesita renderizarse en clientes que bloquean imágenes remotas por defecto (Gmail, Outlook). Un reporte estático que envías por correo a un cliente debe mostrar el logotipo de la empresa incluso sin conexión. Un bookmarklet necesita un ícono que sobreviva al copiar y pegar. Un fragmento de documentación necesita una captura de pantalla que viaje con el markdown. En todos estos casos, la imagen tiene que vivir dentro del documento — y un URI de datos es la forma de lograrlo.

3. Generación programática. El código genera una imagen en tiempo de ejecución — un código QR, un gráfico, una almohadilla de firma — y necesitas mostrarla sin subirla primero a un servidor y obtener una URL de vuelta. canvas.toDataURL('image/png') te entrega un URI de datos directamente; asignarlo a img.src es el flujo de trabajo más simple posible.

// TODOS LOS TIPOS MIME QUE REALMENTE VERÁS

El espacio de tipo MIME acepta cualquier tipo de medio, pero en la práctica verás un puñado pequeño para imágenes:

image/png — el más común. Sin pérdida, soporta transparencia. El Base64 comienza con iVBORw0KGgo.
image/jpeg — fotos y capturas de pantalla. Con pérdida. El Base64 comienza con /9j/.
image/gif — imágenes heredadas y animadas. El Base64 comienza con R0lGOD.
image/webp — moderno, archivos más pequeños. El Base64 comienza con UklGR.
image/svg+xml — gráficos vectoriales. SVG es texto, así que puedes codificarlo en Base64 (con ;base64) o codificarlo en URL (sin él). El SVG codificado en URL suele ser más pequeño.
image/x-icon o image/vnd.microsoft.icon — favicons. El Base64 comienza con 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">

// USANDO URI data: EN HTML, CSS Y JAVASCRIPT

En cualquier lugar donde se acepta una URL, un URI de datos funciona de la misma manera.

<!-- 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 Y BLOB → URI data:

Tres APIs del navegador te entregan un URI de datos sin que tengas que hacer la codificación Base64 tú mismo:

// 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 PENALIZACIÓN DEL 33% EN TAMAÑO (Y CUÁNDO IMPORTA)

Base64 convierte 3 bytes binarios en 4 caracteres ASCII, por lo que la carga útil codificada es exactamente 4⌈n/3⌉ bytes — aproximadamente un 33% más grande que el original. Un PNG de 12 KB se convierte en un URI de datos de ~16 KB, más unos cuantos bytes de sobrecarga por el prefijo data:image/png;base64,.

¿Cuándo importa esto?
Recursos diminutos (menos de 4 KB): el aumento del 33% queda eclipsado por el viaje HTTP ahorrado. Incrústalos sin problema.
Recursos medianos (4–50 KB): caso por caso. Con HTTP/2 + caché, una solicitud separada suele ser más rápida que volver a descargar la versión incrustada en cada carga de página.
Recursos grandes (50 KB+): casi nunca incrustar. El URI de datos infla cada página HTML cacheada, cada correo, cada carga útil JSON que lo contiene. Usa una URL separada.

No hay caché para los URI de datos — el navegador no puede cachear una imagen cuyos bytes están horneados en el HTML. Si la misma imagen aparece en múltiples páginas, cada página paga el costo completo. Un <img src="/logo.png"> regular con cabeceras de caché HTTP se descarga una vez y se reutiliza en todas partes.

// LÍMITES DE LOS NAVEGADORES CON LOS QUE TE TOPARÁS

Los navegadores no imponen un único límite estricto en los URI de datos, pero existen techos en el mundo real:

Chrome / Edge / Firefox: los URI de datos en <img src> funcionan hasta varios megabytes. Más allá de ~32 MB toparás con presión de memoria por pestaña.
Safari: históricamente limitó los URI de datos en <a href> (enlaces de descarga) a alrededor de 2 MB. Para <img>, los tamaños mayores funcionan pero el renderizado se ralentiza.
Internet Explorer 8: 32 KB máximo para los URI de datos en CSS y HTML. (IE9+ eliminó el límite pero nunca fue tan rápido como los navegadores modernos.)
Línea de solicitud HTTP: los URI de datos en <form action> o cadenas de consulta están limitados por la longitud de URL que el navegador acepta (~2 KB para IE, 8 KB+ para otros).

Si te encuentras acercándote a cualquiera de estos límites, el recurso es demasiado grande para incrustarlo. Cambia a una URL regular o a URL.createObjectURL(blob), que te da una URL blob: corta respaldada por un blob que puedes mantener en memoria.

// PREOCUPACIONES DE SEGURIDAD

Los URI de datos no requieren red, lo cual es conveniente — pero saltan varios mecanismos de seguridad web que asumen que el contenido proviene de un servidor.

Política del mismo origen. Un URI data: se considera técnicamente origen opaco (en Chrome/Firefox modernos), por lo que el JavaScript cargado desde un URI de datos no puede acceder a las cookies, localStorage o DOM del documento padre de manera irrestricta. Esto es bueno para aislar contenido no confiable, pero también significa que el rastreo, las analíticas y las protecciones CSRF que dependen de verificaciones de origen pueden comportarse mal.

XSS vía data:text/html. Una URL data:text/html,<script>...</script> ejecutada por un usuario se convierte en una página con privilegios completos. Los navegadores modernos bloquean la navegación de nivel superior a data:text/html por esta razón (Firefox desde 2018, Chrome desde 2020). No pases URLs proporcionadas por el usuario a través de ningún redireccionamiento que permita el esquema data:.

Política de seguridad de contenido (CSP). Por defecto, las CSP estrictas bloquean los URI de datos en img-src, style-src, etc. Para permitirlos, debes incluir explícitamente data: en la directiva (por ejemplo, img-src 'self' data:). Sé deliberado — una vez que permites los URI de datos, también aceptas cualquier imagen (o fuente) que la página decida incrustar.

Registro y PII. Un URI de datos es parte de la URL. Cualquier herramienta de registro o análisis que capture URLs (registros de acceso del servidor, historial del navegador, rastreadores de errores) capturará la carga útil completa. Si tu URI de datos contiene el avatar, foto o captura de pantalla de un usuario, terminará en tus registros. No pongas PII en URLs que no controlas.

// URI data: vs. URL blob: — ¿CUÁL DEBERÍAS USAR?

Ambos te permiten mostrar datos binarios en memoria sin subirlos. La elección depende de dónde necesitan vivir los datos.

Usa URI data: cuando los datos necesiten ser portátiles — incrustados en HTML, enviados en JSON, guardados en una base de datos, copiados y pegados en un correo. Los datos son la URL.

Usa URL blob: cuando los datos sean locales y de corta duración — una vista previa en una aplicación de una sola página, un disparador de descarga, una imagen que desaparece al cerrar la página. URL.createObjectURL(blob) te da un identificador corto (blob:https://example.com/12345-abcde) que el navegador mapea de vuelta al blob en memoria. Mucho más pequeño que un URI de datos, y puedes revocarlo con URL.revokeObjectURL(url) cuando termines.

Regla general: si la URL necesita salir de la página actual, usa data:. Si se queda dentro de la página, usa blob:.

// PROBLEMAS COMUNES DE DEPURACIÓN

Cadena truncada desde DevTools. Cuando copias un URI de datos largo desde la pestaña Network o el panel Elements, los navegadores frecuentemente truncan el valor con puntos suspensivos. El texto mostrado no es la URL completa. Para obtener la cadena completa, abre el archivo fuente (CSS, HTML, JSON) directamente, o usa document.querySelector('img').src en la consola — eso devuelve la URL completa.

Tipo MIME incorrecto. Una carga útil PNG etiquetada como data:image/jpeg;base64,... aún se decodificará a nivel de bytes — a Base64 no le importa el MIME — pero algunos visores rechazan la discrepancia. El formato real se determina por los bytes mágicos (iVBORw0KGgo para PNG), así que cuando tengas dudas, pega la carga útil en nuestro decodificador de Base64 a imagen; detecta automáticamente a partir de los bytes.

Coma faltante. El formato es data:image/png;base64,iVBORw..., no data:image/png;base64;iVBORw.... Un punto y coma o un separador faltante hace que toda la URL sea inválida.

Base64 URL-safe en un URI data:. Los URI data: estándar usan el alfabeto Base64 estándar (con + y /). Si tu carga útil fue codificada con la variante URL-safe (RFC 4648 §5, con - y _), debes convertirla de vuelta antes de incrustarla — consulta nuestra guía Base64 URL-safe vs Standard.

Espacios en blanco dentro de la carga útil. Algunos formateadores envuelven cadenas Base64 largas en 76 columnas con saltos de línea. La mayoría de los navegadores tolera esto en <img src>, pero algunos (especialmente WebViews antiguos) no. Elimina los espacios en blanco antes de incrustar: str.replace(/\s+/g, '').

// CUÁNDO NO USAR URI data:

Por encima de 50 KB. El costo de invalidación de caché + descarga supera el ahorro de la solicitud.
Imágenes repetidas. Cualquier cosa usada en más de una página debe ser una URL regular con cabeceras de caché.
Analíticas de cara al público. Los píxeles de rastreo, balizas y la atribución basada en píxeles no funcionarán porque no hay solicitud HTTP que registrar.
Contenido subido por el usuario mostrado en una SPA. Usa URL.createObjectURL(blob) en su lugar — evita el trabajo de Base64 por completo y es aproximadamente 4× más pequeño en memoria.
Dentro de un entorno con CSP estricta que no permita data:. No agregues data: a tu CSP solo para usar un URI de datos; pregúntate primero si realmente lo necesitas.

// HOJA DE TRUCOS DE 30 SEGUNDOS

Formato: data:[mime-type][;base64],[payload]

Forma más común: data:image/png;base64,iVBORw0KGgo...

Decodifícalo de vuelta a una imagen: pégalo en nuestro conversor de Base64 a imagen.

Codifica una imagen a un URI de datos: usa nuestro conversor de imagen a Base64 — produce el URI data: completo listo para pegar.

Presupuesto de incrustación: < 4 KB siempre incrustar, 4–50 KB depende, > 50 KB usar una URL regular.

API del navegador: canvas.toDataURL(), FileReader.readAsDataURL(), o simplemente construye la cadena tú mismo: 'data:image/png;base64,' + btoa(binaryString).

Lecturas relacionadas: Incrustación de imágenes en Base64 · Vista previa de imágenes Base64 en el navegador · Base64 vs Base64URL vs Base32