Skip to content

Comparison

Base64 vs base64url: dos caracteres que importan

Mismo alfabeto, últimos dos caracteres distintos, relleno diferente. Una línea de diferencia, casos de uso totalmente distintos.

By Published

Resumen. Base64 y base64url son la misma codificación salvo por tres detalles definidos en el RFC 4648: + se convierte en -, / se convierte en _, y el relleno = se elimina. Usa base64url cuando el valor codificado viaje en una URL, JWT o cookie firmada; usa Base64 estándar para adjuntos de correo, HTTP Basic Auth y URIs de datos.

Ambos están definidos en el RFC 4648. El Base64 estándar (§4) y el base64url (§5) difieren en exactamente tres detalles: dos sustituciones de alfabeto y el tratamiento del relleno. Eso es todo. Pero mezclarlos rompe JWT, cookies firmadas y todo lo que viaja en una URL.

La diferencia

PropiedadBase64 estándarBase64url
Carácter 62+-
Carácter 63/_
Relleno= hasta múltiplo de 4Normalmente omitido
¿Seguro para URL?No (+, /, = necesitan codificación porcentual)
Definido enRFC 4648 §4RFC 4648 §5

La misma entrada, ambas codificaciones

Codificando https://example.com/?q=hello world:

  • Estándar: aHR0cHM6Ly9leGFtcGxlLmNvbS8/cT1oZWxsbyB3b3JsZA==
  • Base64url: aHR0cHM6Ly9leGFtcGxlLmNvbS8_cT1oZWxsbyB3b3JsZA

Nótese que / en la forma estándar se convierte en_ en base64url, y el == final se elimina.

Por qué las URL necesitan su propia variante

El alfabeto Base64 estándar incluye + y /. Ambos están reservados en la sintaxis URL:

  • / es el separador de ruta.
  • + se interpreta, por convención en cadenas de consulta codificadas en formulario, como un espacio.
  • = es el delimitador clave-valor en las cadenas de consulta.

Insertar una cadena Base64 estándar en una URL requiere codificar porcentualmente los tres: + se convierte en %2B, / en %2F, = en %3D. La cadena se vuelve más larga y difícil de leer. base64url evita la codificación porcentual eligiendo caracteres que ya son seguros para URL.

Por qué el relleno es opcional en base64url

El Base64 estándar siempre rellena la salida hasta un múltiplo de 4 caracteres usando =. Esto no es matemáticamente necesario — el decodificador puede inferir la longitud original a partir de la longitud de salida módulo 4. El relleno existe para decodificadores en flujo que necesitan conocer el límite entre fragmentos concatenados.

Base64url omite el relleno porque: (a) es redundante, (b) = necesita codificación porcentual en URLs, y (c) los signos de igual al final de las URL a veces confunden a analizadores de rutas, CDN y claves de caché.

Dónde se usa cada uno

Caso de usoVariante
Adjuntos de correo (MIME)Base64 estándar
HTTP Basic Auth (Authorization: Basic …)Base64 estándar
URIs de datos (data:image/png;base64,…)Base64 estándar
JWT (los tres segmentos)Base64url, sin relleno
Verificador/desafío PKCE de OAuth 2.0Base64url, sin relleno
Claves WebPush, JOSE/JWS/JWEBase64url, sin relleno
Cookies firmadas (Rails, Django, etc.)Base64url, sin relleno

El problema con la decodificación

El decodificador Base64 predeterminado de muchas bibliotecas falla con entrada base64url. El base64.b64decode de Python rechaza - y _; el Buffer.from(s, "base64") de Node.js es permisivo pero el inverso (.toString("base64")) produce Base64 estándar.

Solución entre lenguajes: reemplazar antes de decodificar.

// base64url → Base64 estándar
const standard = base64url
  .replace(/-/g, "+")
  .replace(/_/g, "/")
  .padEnd(Math.ceil(base64url.length / 4) * 4, "=");

Mejor: usa una biblioteca que soporte base64url explícitamente (Node 16+ tiene Buffer.from(s, "base64url"); Python tiene base64.urlsafe_b64decode).

La regla práctica

Si el valor codificado aparecerá en una URL, en un JWT, en una cookie firmada o como segmento de ruta — usa base64url. Si es para un adjunto de correo, cabecera HTTP (excepto cookies) o URI de datos en JSON — usa Base64 estándar.

Nuestro codificador Base64 gestiona ambas variantes con un selector de alfabeto. El decodificador JWT asume base64url porque el estándar lo exige.

Datos numéricos

  • Longitud codificada: ambas variantes expanden el binario exactamente 4/3 (ceil(n/3)*4). 100 bytes de entrada → 136 caracteres de salida en Base64 estándar, o 134 sin relleno en base64url.
  • Sobrecarga del relleno: el Base64 estándar añade 0, 1 o 2 caracteres = por cadena codificada — como máximo una penalización del 1,5% para cargas cortas, insignificante para largas.
  • Coste de codificación porcentual en URLs: cada + / = en Base64 estándar se expande a 3 bytes (%2B %2F %3D) al codificarse en URL. Para un JWT de 512 caracteres, son típicamente 30-60 bytes desperdiciados — base64url los evita por completo.
  • Tamaño del alfabeto: 64 caracteres en ambos, por lo que cada carácter de salida lleva exactamente 6 bits de entrada.
  • Tamaños de segmento JWT (RFC 7519): encabezado típico 40-50 caracteres, carga útil 100-400 caracteres, firma HS256 43 caracteres (exactamente 32 bytes / 6 = 43 caracteres base64url, sin relleno).
  • Rendimiento: Buffer.from(s, "base64") de Node 20 decodifica a ~2 GB/s; base64.b64decode de Python a ~600 MB/s en un portátil de 2024.

Matriz de decisión

Dónde vive el valorElige
Ruta URL o cadena de consultabase64url, sin relleno
JWT (encabezado / carga útil / firma)base64url, sin relleno (RFC 7519)
Verificador PKCE OAuth 2.0base64url, sin relleno (RFC 7636 §4.1)
Cookie firmada (Rails / Django / Express)base64url, sin relleno
URI data: en HTML/CSSBase64 estándar con relleno
Adjunto de correo MIMEBase64 estándar con relleno, ajuste de 76 caracteres (RFC 2045)
Cabecera HTTP Basic AuthBase64 estándar (RFC 7617)
Claves de suscripción WebPushbase64url, sin relleno (RFC 8291)

Fuentes

Frequently asked questions

¿Puedo decodificar base64url con un decodificador Base64 estándar?
A veces — Buffer.from de Node es permisivo, b64decode de Python no lo es. El enfoque seguro entre lenguajes: reemplaza ‘-’ por ‘+’, ‘_’ por ‘/’, y rellena hasta múltiplo de 4 con ‘=’ antes de decodificar. Los entornos modernos ofrecen decodificadores base64url explícitos (Node 16+, urlsafe_b64decode de Python) que gestionan esto directamente.
¿Por qué base64url elimina el relleno?
Por tres razones: es matemáticamente redundante (los decodificadores pueden inferir la longitud original a partir de la longitud de salida módulo 4), el carácter ‘=’ necesita codificación porcentual en URLs, y el ‘=’ final confunde a algunos analizadores de rutas, CDN y claves de caché.
¿Los segmentos de JWT son siempre base64url?
Sí. El RFC 7519 exige base64url sin relleno para los tres segmentos del JWT (encabezado, carga útil, firma). Codificar un JWT con Base64 estándar produce un token que ninguna biblioteca conforme verificará.
¿Base64url ahorra bytes respecto a Base64 estándar?
Marginalmente — el mismo tamaño de alfabeto implica la misma longitud codificada antes del relleno. Eliminar el relleno ahorra 0-2 bytes por codificación. El ahorro real proviene de evitar la codificación porcentual (cada %XX son 3 bytes por 1), que puede ahorrar 6-9 bytes por cadena Base64 en una URL.

Related

Published May 16, 2026