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.
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 headers —
Authorization: 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.