Base64 encoding explained: URL-safe mode, padding, and common mistakes

Everything developers need to know about Base64 — how the encoding works, the difference between standard and URL-safe variants, padding rules, and the most common pitfalls.

J
Jan Stepien·

Base64 shows up everywhere in web development: JWTs, data URLs, email attachments, API keys, binary data in JSON, and more. Despite being one of the most-used encodings in software, it trips up developers regularly — usually over padding, the URL-safe variant, or a mistaken belief that it provides some kind of encryption. This guide covers how Base64 works, when to use which variant, and the mistakes that appear most often in the wild.

How Base64 encoding works

Base64 converts arbitrary binary data into a string made up of 64 printable ASCII characters: A–Z (26), a–z (26), 0–9 (10), + (1), / (1). The name comes from this alphabet of 64 characters.

The algorithm takes 3 bytes of input (24 bits) at a time and encodes them as 4 characters (6 bits each). Because every 3 bytes become 4 characters, Base64 output is always 4/3 (approximately 133%) the size of the input — encoding increases size by about 33%.

Input bytes:   M        a        n
               01001101 01100001 01101110

Split into 6-bit groups:
               010011 010110 000101 101110

Map to Base64 alphabet:
               T      W      F      u

Result: "TWFu"

When the input length is not a multiple of 3, padding is added. One extra byte → two characters + ==. Two extra bytes → three characters + =. This is why Base64 strings frequently end in one or two equals signs.

Standard vs URL-safe Base64

The two characters in the standard alphabet that are not URL-safe are + and /. Both have special meanings in URLs: + is a space in query strings, and / is a path separator. If you put a standard Base64 string into a URL without escaping it, these characters will be misinterpreted.

The URL-safe variant (defined in RFC 4648 §5) replaces these two characters:

StandardURL-safeNotes
+-hyphen-minus
/_underscore
=(often omitted)padding stripped in URL contexts

JWTs use URL-safe Base64 without padding (called "Base64url" in RFC 7515). If you try to decode a JWT part with a standard Base64 decoder, it will fail for any token that contains a - or _ — you first need to replace them back to + and /.

Encoding in JavaScript

The browser's built-in btoa() function encodes standard Base64. Its inverse is atob(). Neither is URL-safe, and both fail on non-Latin-1 input.

// Standard Base64 — browser built-in
const encoded = btoa("Hello, world!");       // "SGVsbG8sIHdvcmxkIQ=="
const decoded = atob("SGVsbG8sIHdvcmxkIQ=="); // "Hello, world!"

// URL-safe Base64 (RFC 4648 §5) — no padding
function toBase64Url(str) {
  return btoa(str)
    .replace(/+/g, '-')
    .replace(///g, '_')
    .replace(/=/g, '');
}

function fromBase64Url(str) {
  // Add back padding
  const padded = str + '==='.slice((str.length + 3) % 4);
  return atob(padded.replace(/-/g, '+').replace(/_/g, '/'));
}

// Unicode-safe encoding (btoa fails on emoji / non-Latin-1)
function encodeBase64(str) {
  return btoa(
    encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, (_, p1) =>
      String.fromCharCode(parseInt(p1, 16))
    )
  );
}

// Node.js built-in (no btoa/atob needed)
Buffer.from("Hello").toString("base64");            // "SGVsbG8="
Buffer.from("SGVsbG8=", "base64").toString("utf8"); // "Hello"

Common use cases

  • JWTs: all three parts (header, payload, signature) are Base64url-encoded. The JWT Decoder handles the decoding and re-adds missing padding automatically.
  • Data URLs: embedding images, fonts, or other binary data directly in HTML/CSS — data:image/png;base64,iVBORw0KGgo.... Useful for small assets to reduce HTTP requests; avoid for large files.
  • Binary data in JSON: JSON has no binary type, so binary payloads (images, certificates, encrypted blobs) are commonly Base64-encoded when sent as JSON fields.
  • HTTP Basic Auth: the Authorization: Basicheader encodes username:password as standard Base64. Never rely on this for confidentiality — it is trivially reversible.
  • Email (MIME): attachments are Base64-encoded so binary files can travel safely through text-based email protocols.

Common mistakes

  • Thinking Base64 is encryption: it is not. Anyone can decode a Base64 string in seconds. It is encoding for text-safe transport, not confidentiality. Sensitive data in Base64 is just as exposed as plaintext.
  • Mixing standard and URL-safe variants: the most common bug is decoding a Base64url string with a standard decoder. Check whether your string contains - or _ — if it does, it is URL-safe and needs conversion first.
  • Missing padding: atob() requires padding. If you stripped = for URL cleanliness, re-add it before decoding: str + '==='.slice((str.length + 3) % 4).
  • Passing Unicode to btoa(): btoa()only handles Latin-1 characters. Anything outside that range (emoji, Chinese, Arabic, etc.) throws aDOMException. Use the TextEncoder + Uint8Array approach or the encode/decode helpers above.
  • Double-encoding: encoding an already-encoded string produces garbage on decode. Common when a middleware layer and the application both Base64-encode the same value before sending.

When to use URL encoding instead

Base64 and URL encoding (percent-encoding) both make data safe for transmission, but they serve different purposes. Use Base64 when you need to encode binary data as a text string for embedding or transport. Use URL encoding when you need to make a text string safe for use in a URL — specifically when the string contains characters like spaces, ampersands, equals signs, or non-ASCII characters that have special meaning in URLs.

Confusing the two is a frequent source of bugs. A URL query parameter value should be percent-encoded, not Base64-encoded. A binary file embedded in a JSON body should be Base64-encoded, not percent-encoded.

Try it now

Encode or decode any Base64 string — standard or URL-safe — at quickhelp.dev/base64. The tool detects which variant your input uses, handles padding automatically, and shows both the encoded and decoded values side by side. It also works as an API: POST /api/base64 with {"input":"...","mode":"encode","variant":"url-safe"}.

We use cookies to serve ads and measure traffic. Cookie policy · Privacy policy