btoa y atob siguen siendo latin1 y provocan mojibake en datos UTF-8
La función btoa/atob del navegador sigue operando con cadenas binarias en latin‑1, lo que hace que los caracteres fuera de U+00FF se codifiquen incorrectamente y generen errores silenciosos al decodificar.

btoa('café') devuelve Y2Fm6Q==, mientras que la versión correcta en UTF‑8 es Y2Fmw6k=. El problema se debe a que btoa y atob están diseñados para trabajar con "binary strings" donde cada carácter ocupa un byte. Si el carácter no cabe en un byte (por ejemplo, é = U+00E9), btoa la representa en latin‑1 (0xE9) y no en UTF‑8 (0xC3 0xA9). El resultado es que la cadena decodificada con atob produce mojibake, pero sin lanzar error.
¿Qué pasa con los errores?
- Codificación: Si el carácter excede U+00FF, btoa lanza
InvalidCharacterError. Los emojis y los textos CJK fallan de inmediato. - Decodificación: atob nunca lanza error; simplemente devuelve la secuencia de bytes y el resto del proceso interpreta esos bytes como si fueran UTF‑8, produciendo caracteres extraños.
Solución recomendada
Para evitar estos problemas, convierta explícitamente el texto a bytes UTF‑8 antes de usar btoa y decodifique con un TextDecoder configurado como fatal:
const UTF8_ENCODE = new TextEncoder();
const UTF8_DECODE = new TextDecoder('utf-8', { fatal: true });
function encodeBase64(text) {
const bytes = UTF8_ENCODE.encode(text);
const CHUNK = 0x8000;
let binary = '';
for (let i = 0; i < bytes.length; i += CHUNK) {
binary += String.fromCharCode.apply(null, bytes.subarray(i, i + CHUNK));
}
return btoa(binary);
}
function decodeBase64(b64) {
const bytes = Uint8Array.from(atob(b64), c => c.charCodeAt(0));
return UTF8_DECODE.decode(bytes);
}
Puntos clave
fatal: truehace que el decoder lance error ante bytes inválidos, evitando que se devuelvan sustitutos.- No se debe usar
String.fromCharCode(...bytes)con arreglos grandes; el límite de la pila varía entre motores. - Se recomienda aceptar ambos alfabetos de base64 (RFC 4648 §4 y §5) y usar
TextDecoderpara detectar caracteres no válidos.
Por qué importa
Muchas aplicaciones, especialmente aquellas que generan tokens JWT o comunican datos entre servicios, confían en btoa/atob sin considerar el soporte UTF‑8. Cuando los datos se persisten en bases de datos o se transmiten a sistemas externos, el mojibake puede propagarse y causar errores difíciles de rastrear. Al implementar la solución propuesta, los equipos garantizan que la codificación y decodificación de base64 sea consistente y segura, evitando pérdidas de datos y fallos silenciosos en producción.
En resumen, btoa y atob siguen siendo herramientas legacy de latin‑1. Para trabajar con UTF‑8, convierta el texto a bytes explícitamente y use un decoder fatal para detectar errores. Así se evita la propagación de mojibake y se mantiene la integridad de los datos en entornos modernos.
