Cómo Decodificar Base64 en JavaScript
Decodifica Base64 en JavaScript correctamente: atob en el navegador, Buffer en Node.js, TextDecoder seguro para Unicode, variantes URL-safe y manejo de errores.
Decodificar Base64 en JavaScript parece cosa de una sola línea, y para ASCII puro lo es. Pero en cuanto decodificas emojis, caracteres acentuados, el payload de un JWT o una cadena URL-safe, el enfoque ingenuo se rompe de formas sutiles y frustrantes. Esta guía recorre todos los métodos fiables, tanto en el navegador como en Node.js, con los casos límite que de verdad afectan a los desarrolladores en producción.
Si solo necesitas un resultado ahora mismo sin escribir código, pega tu cadena en Base64 Decode y lee el texto plano al instante. Para todo lo demás, aquí tienes cómo hacerlo bien con código.
La respuesta rápida: atob()
Todos los navegadores (y Node.js moderno) exponen dos funciones globales:
btoa()— binary to aSCII (codificar)atob()— aSCII to binary (decodificar)
atob('SGVsbG8sIFdvcmxkIQ=='); // "Hello, World!"
btoa('Hello, World!'); // "SGVsbG8sIFdvcmxkIQ=="
Para texto ASCII sencillo, esto es todo lo que necesitas. El problema es que atob() devuelve una cadena binaria: una cadena en la que cada carácter representa un byte (0–255). Eso funciona a la perfección con caracteres latinos, pero estropea cualquier cosa fuera del rango ASCII.
El problema de Unicode
Intenta decodificar una cadena Base64 que contenga un carácter no ASCII y verás basura:
// "Héllo" codificado como UTF-8 y luego Base64 es "SMOpbGxv"
atob('SMOpbGxv'); // "Héllo" ❌ mojibake
La é acentuada salió como dos caracteres sueltos. ¿Por qué? Porque atob() te da bytes en bruto, y la é en UTF-8 son dos bytes (0xC3 0xA9). Tratar esos dos bytes como dos caracteres separados produce el clásico mojibake é.
La solución es tomar los bytes de atob() y decodificarlos como UTF-8 de forma explícita.
La forma moderna y correcta: TextDecoder
function decodeBase64Utf8(b64) {
const binary = atob(b64);
const bytes = Uint8Array.from(binary, c => c.charCodeAt(0));
return new TextDecoder('utf-8').decode(bytes);
}
decodeBase64Utf8('SMOpbGxv'); // "Héllo" ✅
TextDecoder está integrado en todos los navegadores modernos y en Node.js. Es el enfoque recomendado en 2024 y en adelante: rápido, basado en estándares y correcto para todo el rango Unicode, incluidos los emojis.
// "🚀 launch" hace el viaje de ida y vuelta correctamente
const bytes = new TextEncoder().encode('🚀 launch');
const b64 = btoa(String.fromCharCode(...bytes));
decodeBase64Utf8(b64); // "🚀 launch" ✅
Decodificar Base64 en Node.js
Node.js tiene su propia API, más limpia, a través de la clase Buffer. No necesitas atob() en absoluto (aunque existe como global desde Node 16).
// Decodificar Base64 a una cadena UTF-8
Buffer.from('SMOpbGxv', 'base64').toString('utf-8'); // "Héllo"
// Codificar una cadena UTF-8 a Base64
Buffer.from('Héllo', 'utf-8').toString('base64'); // "SMOpbGxv"
Buffer maneja Unicode correctamente de fábrica porque le indicas la codificación de salida ('utf-8'). Esta es la forma idiomática de decodificar Base64 en cualquier JavaScript del lado del servidor, incluidos los manejadores de Express, las funciones serverless y los scripts de build.
Decodificar a bytes en bruto (archivos, imágenes)
Cuando el Base64 representa datos binarios —una imagen, un PDF, una clave criptográfica— no lo conviertas a una cadena. Mantenlo como un Buffer:
import { writeFileSync } from 'node:fs';
const b64 = 'iVBORw0KGgoAAAANSUhEUgAA...'; // un PNG, codificado
const buffer = Buffer.from(b64, 'base64');
writeFileSync('output.png', buffer); // escribe la imagen real
Convertir datos binarios a una cadena UTF-8 los corrompería, porque la mayoría de las secuencias de bytes no son UTF-8 válido.
Manejar Base64 URL-safe
Los JWT, los tokens OAuth y muchas APIs usan Base64 URL-safe (RFC 4648 §5), que intercambia dos caracteres y a menudo elimina el relleno:
| Estándar | URL-safe |
|---|---|
+ |
- |
/ |
_ |
relleno = |
normalmente omitido |
atob() nativo y Buffer.from(..., 'base64') esperan el alfabeto estándar, así que primero debes normalizar:
function decodeBase64Url(b64url) {
// 1. Restaurar el alfabeto estándar
let b64 = b64url.replace(/-/g, '+').replace(/_/g, '/');
// 2. Volver a añadir el relleno para que la longitud sea múltiplo de 4
while (b64.length % 4) b64 += '=';
// 3. Decodificar como UTF-8
const bytes = Uint8Array.from(atob(b64), c => c.charCodeAt(0));
return new TextDecoder('utf-8').decode(bytes);
}
// Decodificar el segmento de payload de un JWT
const payload = 'eyJ1c2VyIjoiam9obiIsImFkbWluIjp0cnVlfQ';
decodeBase64Url(payload); // '{"user":"john","admin":true}'
Node 16+ también acepta directamente la codificación 'base64url', que se encarga de todo esto por ti:
Buffer.from('eyJ1c2VyIjoiam9obiJ9', 'base64url').toString('utf-8');
// '{"user":"john"}'
Si quieres inspeccionar un token sin escribir código, el decodificador de JWT hace por ti la división y la decodificación Base64URL. Para entender el panorama general de cómo se relacionan estos formatos, consulta codificar vs decodificar.
Manejo de errores
atob() lanza una DOMException ("InvalidCharacterError") cuando la entrada no es Base64 válido. Envuelve siempre la decodificación en una protección, sobre todo cuando la entrada procede de usuarios o de APIs externas:
function safeDecode(b64) {
try {
const bytes = Uint8Array.from(atob(b64), c => c.charCodeAt(0));
return new TextDecoder('utf-8', { fatal: true }).decode(bytes);
} catch (err) {
return null; // Base64 inválido o UTF-8 inválido
}
}
Pasar { fatal: true } a TextDecoder hace que lance una excepción ante secuencias de bytes mal formadas en lugar de insertar silenciosamente caracteres de reemplazo (�). Eso suele ser lo que quieres: fallar de forma ruidosa en lugar de entregar texto corrupto.
Causas habituales de fallos al decodificar:
- Espacios en blanco o saltos de línea en la cadena: elimínalos con
.replace(/\s/g, '')antes de decodificar (Base64 MIME divide las líneas a los 76 caracteres). - Relleno ausente: vuelve a añadir
=hasta que la longitud sea divisible entre 4. - Caracteres URL-safe (
-,_) pasados a un decodificador estándar: normalízalos primero. - Datos codificados dos veces: alguien codificó una cadena que ya estaba codificada.
Una función reutilizable y lista para producción
Aquí tienes una única función que maneja entrada estándar y URL-safe, espacios en blanco, relleno ausente y Unicode, apta tanto para el navegador como para Node.js moderno:
function decodeBase64(input) {
let b64 = input.trim()
.replace(/\s+/g, '') // eliminar saltos de línea MIME
.replace(/-/g, '+') // URL-safe → estándar
.replace(/_/g, '/');
while (b64.length % 4) b64 += '='; // corregir el relleno
const bytes = Uint8Array.from(atob(b64), c => c.charCodeAt(0));
return new TextDecoder('utf-8', { fatal: true }).decode(bytes);
}
Añádela a cualquier proyecto y se comportará de forma predecible frente a las cadenas desordenadas del mundo real que realmente recibes.
Preguntas frecuentes
¿Por qué atob() devuelve caracteres extraños para texto acentuado o emojis?
Porque atob() devuelve una cadena binaria de bytes en bruto, no texto decodificado. Los caracteres UTF-8 multibyte (acentos, emojis, CJK) vuelven como bytes separados y se muestran como mojibake. Pasa los bytes por TextDecoder('utf-8') para recuperar el texto original.
¿Está obsoleto atob()?
No. atob() y btoa() siguen siendo estándar y están soportados en todas partes. Solo que son de bajo nivel: operan sobre cadenas binarias, así que los combinas con TextDecoder/TextEncoder para Unicode. En Node.js, Buffer es la opción más ergonómica.
¿Cómo decodifico Base64 en Node.js sin atob()?
Usa Buffer.from(str, 'base64').toString('utf-8') para texto, o mantén el Buffer tal cual para datos binarios como imágenes y archivos. Para entrada URL-safe, usa la codificación 'base64url' (Node 16+).
¿Cómo decodifico el payload de un JWT en JavaScript?
Divide el token por ., toma el segundo segmento (el payload) y decodifícalo como Base64 URL-safe; después aplica JSON.parse() al resultado. Ten en cuenta que esto solo lee el token: no verifica la firma. Usa el decodificador de JWT para inspeccionar tokens visualmente.
¿Cuál es la diferencia entre Base64 estándar y URL-safe?
El Base64 estándar usa + y / y rellena con =. El Base64 URL-safe los reemplaza por - y _ y normalmente omite el relleno, de modo que la cadena puede viajar en URLs y nombres de archivo sin escapado. Normaliza la entrada URL-safe de vuelta al alfabeto estándar antes de decodificar con atob().
¿Cómo puedo decodificar Base64 sin escribir nada de código?
Pega la cadena en Base64 Decode y el texto plano aparece al instante en tu navegador. Maneja automáticamente los espacios en blanco, los caracteres URL-safe y el relleno ausente, y ningún dato sale jamás de tu dispositivo.
Pruébalo tú mismo
Usa nuestra herramienta online gratuita para empezar de inmediato