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

  1. Convierte la cadena con new TextEncoder().encode(text).
  2. Transforma los bytes a Base64 o Base64URL según el contrato.
  3. Para volver, valida primero alfabeto, padding y bits finales.
  4. Decodifica Base64 a bytes.
  5. 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.