Guía
Base64 y UTF-8: codificar tildes, ñ y emojis correctamente
Entiende por qué btoa falla o produce mojibake con Unicode y cómo convertir texto UTF-8 y Base64 sin perder caracteres.
por Tools in a Tab · Publicado el · Revisado el
Respuesta breve
Base64 codifica bytes, no letras. Para conservar á, ñ, alfabetos no
latinos o emojis, convierte primero el texto a bytes UTF-8 y aplica Base64 a
esos bytes. Al decodificar, recupera los bytes y exige UTF-8 válido. Tratar cada
unidad JavaScript como un byte provoca errores, caracteres corruptos o el
conocido mojibake.
El paso que suele faltar
La cadena España 🌍 es texto Unicode. UTF-8 define qué bytes representan cada
carácter; después Base64 transforma esos bytes en caracteres ASCII aptos para
transporte. Son dos capas distintas:
texto Unicode → bytes UTF-8 → Base64
La vuelta debe respetar exactamente el orden contrario:
Base64 → bytes → decodificación UTF-8 estricta
El conversor Base64 y Base64URL realiza esos pasos localmente. Si los bytes finales no forman UTF-8 válido, informa del problema en lugar de inventar símbolos de sustitución.
Por qué btoa() falla con algunos caracteres
La función histórica btoa() del navegador interpreta cada unidad de la
cadena como un valor de un solo byte. Puede trabajar directamente con ASCII y
cierto rango Latin-1, pero un emoji o muchos caracteres Unicode no caben en ese
modelo. Entonces puede lanzar InvalidCharacterError; con adaptaciones
incompletas también puede producir texto ilegible.
En código moderno, el patrón correcto usa TextEncoder para obtener UTF-8 y
TextDecoder con comportamiento fatal para comprobar la vuelta. No basta con
llamar a encodeURIComponent y manipular escapes: esa receta antigua es más
difícil de auditar y puede fallar con entradas mal formadas.
Ejemplos que revelan el problema
Hola usa los mismos valores básicos en ASCII y UTF-8, por lo que muchos
errores quedan ocultos. Prueba siempre casos como Málaga, niño, 東京 y
👩🏽💻. Un test útil exige que decodificar el resultado devuelva la misma
secuencia Unicode, no solo algo visualmente parecido.
Por ejemplo, ñ se representa en UTF-8 con los bytes hexadecimales C3 B1.
Su Base64 es w7E=. Si alguien interpreta esos bytes por separado como Latin-1,
puede aparecer ñ: el Base64 era correcto, pero la decodificación de texto no.
UTF-8 inválido no es texto misterioso
Base64 puede transportar cualquier byte, incluidos bytes de una imagen, un ZIP o una codificación distinta. Que una cadena Base64 sea válida no garantiza que su contenido sea UTF-8. Por eso una herramienta centrada en texto debe separar dos diagnósticos: formato Base64 inválido y bytes válidos que no forman texto UTF-8.
La Encoding Standard de WHATWG define el algoritmo UTF-8 y el tratamiento de errores. Si conoces que el origen usa Windows-1252 u otra codificación, conviértelo con esa información antes de esperar texto Unicode correcto.
Procedimiento recomendado en JavaScript
- Convierte la cadena con
new TextEncoder().encode(text). - Transforma los bytes a Base64 o Base64URL según el contrato.
- Para volver, valida primero alfabeto, padding y bits finales.
- Decodifica Base64 a bytes.
- Usa
new TextDecoder('utf-8', { fatal: true })para rechazar secuencias inválidas.
No confundas caracteres con glifos: algunos emojis se forman con varias unidades Unicode unidas. No necesitas dividirlos para Base64; debes entregar la cadena completa al codificador UTF-8.
Cómo evitar mojibake en una API
Documenta la codificación junto al campo, no solo «Base64». Si el contenido es texto, especifica UTF-8; si es un archivo, conserva su tipo MIME y trata la salida como bytes. Al depurar, compara primero los bytes hexadecimales del origen y el destino. Esa comparación localiza si la corrupción ocurrió antes de Base64, durante el transporte o al volver a interpretar los bytes.