Base64 explained

Base64 is encoding, not encryption. Here's how it works, why it inflates size by ~33%, standard vs URL-safe, Unicode handling, and the four places it actually belongs.

5 min read

Base64 has one job: represent arbitrary bytes using only 64 printable ASCII characters. It's not encryption, it's not compression, and using it as either is a bug. Here's what it actually is, why it exists, and the four places it belongs.

How the encoding works

Take 3 bytes (24 bits) of input. Split them into 4 groups of 6 bits. Look each group up in a 64-character alphabet: A–Z, a–z, 0–9, +, /. Output those 4 characters. When the input isn't a multiple of 3 bytes, pad the output with =.

That's the entire algorithm. The 4:3 output-to-input ratio is why Base64 inflates size by ~33% — a fact worth remembering before wrapping a large blob in it.

Standard vs URL-safe

The default alphabet uses +, /, and =, all of which have special meaning in URLs. URL-safe Base64 (RFC 4648 §5) swaps them for -, _, and usually drops the padding. Convert between the two with a simple character swap:

// standard → URL-safe
s.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "")

// URL-safe → standard (add padding back)
s.replace(/-/g, "+").replace(/_/g, "/") + "===".slice((s.length + 3) % 4)

Where Base64 belongs

  • HTTP Basic auth headersAuthorization: Basic base64(user:pass).
  • Data URIs — inlining small images or fonts into HTML/CSS.
  • JWT parts — header and payload are base64url-encoded JSON. See JWT explained.
  • Binary in text-only protocols — email attachments (MIME), JSON payloads that must carry a small binary blob.

Where Base64 does NOT belong

  • As encryption or obfuscation. Anyone can decode Base64 in one call. It's the digital equivalent of writing a secret in a different font.
  • To "compress" data. It grows the payload, doesn't shrink it. If you need smaller, gzip first — Base64 after if a text channel is unavoidable.
  • For large files over HTTP. Sending a 10 MB image as Base64 inside JSON turns it into 13.3 MB and forces the whole thing to be parsed as a string. Upload the file as multipart/form-data.
  • As a database primary key. Use a UUID or an integer. Base64 keys look identifier-ish but sort meaninglessly and encode nothing useful.

Handling Unicode

Base64 encodes bytes, not characters. Before encoding Unicode text you must UTF-8 encode it first, or multi-byte characters (emoji, accented letters, CJK) get mangled. In the browser, btoa alone doesn't do this; use TextEncoder or the built-in Base64 encoder which handles it for you.

// safe UTF-8 → Base64 in browser JS
btoa(String.fromCharCode(...new TextEncoder().encode(input)));

// safe Base64 → UTF-8
new TextDecoder().decode(Uint8Array.from(atob(input), c => c.charCodeAt(0)));

Padding and length rules

  • Standard Base64 output length is always a multiple of 4.
  • = padding tells the decoder how many bytes to discard from the last group (0, 1, or 2).
  • URL-safe Base64 usually drops padding. Add it back before decoding if your library complains.

Bottom line

Base64 is a transport format for bytes. Reach for it when you have binary that must travel through a text-only channel — nothing more, nothing less. Anything you'd call "clever" with Base64 is probably a bug in disguise.

Try the tools

Related reading