Toolbit
Guías

Base64 explicado: cómo funciona, el relleno y sus variantes

Cómo convierte Base64 cada 3 bytes en 4 caracteres, por qué aparecen = y ==, qué cambia en base64url y los errores más comunes al usarlo desde el código.

Por Javier VallejoPublicado el 7 min de lectura

El problema que resuelve Base64

Los primeros sistemas de correo electrónico se diseñaron para transportar texto en ASCII de 7 bits. Cualquier byte con un valor por encima de 127, o un carácter de control en el lugar equivocado, podía alterarse o perderse en algún servidor intermedio. Cuando surgió la necesidad de enviar archivos adjuntos (imágenes, documentos, programas, que son secuencias arbitrarias de bytes), hizo falta una forma de disfrazar bytes de texto inofensivo. El estándar MIME (RFC 2045, de 1996) adoptó Base64 para eso, y el mismo truco se volvió la solución estándar cada vez que hay que meter datos binarios en un canal pensado para texto: cabeceras HTTP, JSON, XML, URL, archivos de configuración.

Esta guía muestra cómo funciona Base64 bit a bit con ejemplos resueltos, por qué aparecen los signos = al final, qué variantes existen y cuáles son los errores más comunes al usarlo desde el código. Para codificar o decodificar un valor concreto, el codificador Base64 lo hace en el navegador.

Codificar a mano: "Man" → "TWFu"

El ejemplo clásico es la palabra Man. Son tres caracteres ASCII, es decir, tres bytes:

CarácterDecimalBinario
M7701001101
a9701100001
n11001101110

Base64 pone los 24 bits uno detrás del otro y los vuelve a cortar, esta vez en cuatro grupos de 6 bits:

01001101 01100001 01101110      ← 3 bytes (8 bits cada uno)
010011 010110 000101 101110     ← 4 grupos de 6 bits
  19     22      5     46       ← valor de cada grupo
  T      W       F     u        ← símbolo del alfabeto

Cada valor de 0 a 63 se traduce con el alfabeto: A–Z son 0–25, a–z son 26–51, 0–9 son 52–61, y + y / son 62 y 63. El 19 es T; el 22, W; el 5, F; y el 46 cae en las minúsculas: 46 − 26 = 20, y la letra en la posición 20 contando desde la a como 0 es la u. Resultado: TWFu.

Si se quieren comprobar las conversiones de binario a decimal de cada grupo, el conversor de bases muestra cualquier valor en las cuatro bases a la vez.

El relleno: por qué aparecen = y ==

El método anterior necesita bloques completos de 3 bytes. Cuando la entrada no es múltiplo de 3, el último bloque queda incompleto y se resuelve así: se agregan bits en cero hasta completar el último grupo de 6, y los caracteres que faltan para llegar a 4 se reemplazan por =.

Dos bytes, "Ma": son 16 bits. Se agregan dos ceros para llegar a 18 (tres grupos de 6) y se completa con un =:

01001101 01100001 00            ← 16 bits + 2 ceros de relleno
010011 010110 000100            ← 3 grupos
  T      W      E      =        ← TWE=

Un byte, "M": son 8 bits. Se agregan cuatro ceros para llegar a 12 (dos grupos) y se completa con ==:

01001101 0000                   ← 8 bits + 4 ceros de relleno
010011 010000                   ← 2 grupos
  T      Q      =      =        ← TQ==

La regla es fácil de recordar: == significa que sobró 1 byte, = que sobraron 2, y sin relleno, que la entrada era múltiplo de 3. El relleno no agrega información; solo deja la longitud en un múltiplo de 4, y por eso muchas implementaciones lo aceptan ausente y algunas variantes directamente lo omiten.

Decodificar a mano

El proceso inverso es simétrico: cada carácter se traduce a su valor de 6 bits, se concatenan y se cortan en bytes de 8. Con TWFu:

T=19   W=22   F=5    u=46
010011 010110 000101 101110
01001101 01100001 01101110  →  77 97 110  →  "Man"

Si hay =, se descartan los bits de relleno sobrantes. Un detalle útil para detectar errores: una cadena Base64 cuyo largo (sin contar espacios) deja resto 1 al dividirla por 4 es necesariamente inválida, porque un solo carácter aporta 6 bits y no alcanza para formar un byte.

Cuánto ocupa

Cada 3 bytes se convierten en 4 caracteres, así que el tamaño crece un 33 % (4/3). En formatos que cortan el texto en líneas, como MIME con 76 caracteres por línea más el salto \r\n, el crecimiento real ronda el 37 %. Por eso Base64 no es buena idea para transportar archivos grandes dentro de JSON: una API que recibe documentos de varios megas suele funcionar mejor con multipart/form-data o con una subida directa del archivo binario.

Las variantes

Todas usan el mismo mecanismo de 6 bits; cambian el alfabeto o el formato:

VarianteDefinida enDiferencia
Base64 estándarRFC 4648, sección 4Alfabeto con + y /, relleno con =
base64urlRFC 4648, sección 5- y _ en lugar de + y /; relleno normalmente omitido
MIMERFC 2045Estándar, con líneas de 76 caracteres como máximo
PEMRFC 7468Estándar, con líneas de 64 caracteres entre -----BEGIN …----- y -----END …-----

La variante base64url existe porque +, / y = tienen significado en las URL. Un token en Base64 estándar puesto en una query string sin más puede llegar alterado: muchos servidores interpretan el + como un espacio.

Dónde aparece en la práctica

Autenticación HTTP Basic. El RFC 7617 usa como ejemplo el usuario Aladdin con la contraseña open sesame. La cabecera que envía el navegador es:

Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==

Ese valor es Aladdin:open sesame en Base64, nada más. Cualquiera que intercepte la petición lee la contraseña al instante, y por eso Basic solo es aceptable sobre HTTPS.

JSON Web Tokens. Un JWT tiene tres partes separadas por puntos. La primera, en un token típico, es eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9, que en base64url decodifica a {"alg":"HS256","typ":"JWT"}. La cabecera y el contenido de un JWT son públicos para quien tenga el token; lo único que garantiza la firma es que nadie los modificó.

Data URIs. data:image/svg+xml;base64,PHN2Zy... permite incrustar un archivo dentro de HTML o CSS. Funciona bien para íconos pequeños; para imágenes grandes, el 33 % extra y la imposibilidad de cachearlas por separado suelen salir caros.

Base64 desde el código

EntornoCodificar texto UTF-8Decodificar
Node.jsBuffer.from(texto, "utf8").toString("base64")Buffer.from(b64, "base64").toString("utf8")
Node.js (base64url)Buffer.from(texto).toString("base64url")Buffer.from(b64, "base64url")
Pythonbase64.b64encode(texto.encode()).decode()base64.b64decode(b64).decode()
Python (base64url)base64.urlsafe_b64encode(datos)base64.urlsafe_b64decode(b64)
Bashprintf '%s' "$texto" | base64base64 -d

En el navegador, btoa() y atob() existen desde hace décadas pero trabajan con "cadenas binarias", donde cada carácter representa un byte. Con texto que no sea ASCII fallan o dan un resultado distinto del esperado, así que primero hay que convertir el texto a bytes con TextEncoder.

Errores frecuentes

  • Usarlo como si fuera cifrado. Guardar contraseñas, claves de API o datos personales "en Base64" no los protege en absoluto. Es tan reversible como leer un número en hexadecimal.
  • El salto de línea de echo. echo 'Hola' | base64 codifica Hola\n y produce SG9sYQo=. Con printf o echo -n se obtiene SG9sYQ==. Es la causa más común de "mi Base64 no coincide".
  • Latin-1 en lugar de UTF-8. btoa("ñ") devuelve 8Q==, que es la ñ en Latin-1. En UTF-8, que es lo que espera casi cualquier sistema actual, la ñ es w7E=. Y btoa("€") directamente lanza un InvalidCharacterError.
  • Quitar el relleno y no volver a ponerlo. Muchos decodificadores estrictos, como base64.b64decode de Python, fallan con Incorrect padding si falta el =. Al recibir base64url sin relleno, hay que completarlo hasta un múltiplo de 4 antes de decodificar.
  • Mezclar variantes. Un decodificador estándar estricto rechaza - y _, y uno de base64url rechaza + y /. Cuando la fuente no está clara, conviene normalizar antes.
  • Codificar dos veces. SGVsbG8= codificado otra vez da U0dWc2JHOD0=. Si un valor decodificado sigue pareciendo Base64, probablemente se codificó dos veces en algún punto del camino.

Resumen

Base64 convierte cada 3 bytes en 4 caracteres de un alfabeto de 64 símbolos, de modo que cualquier dato binario pueda viajar por un canal de texto. El relleno = completa el último bloque, base64url cambia dos símbolos para que el resultado sea seguro en URL, y el costo es un 33 % de tamaño extra. No protege nada: es transporte, no seguridad.

Herramientas usadas en esta guía

Guías relacionadas