Base64 is one of those technologies that is everywhere and explained almost nowhere: email attachments, embedded images, authentication headers, web tokens and API payloads all carry it. It is also chronically misunderstood — treated as encryption by beginners and as bloat by everyone else. This guide explains the problem Base64 solves, the elegant little mechanism it uses, and the variants and pitfalls that matter in practice. If you just need to convert something, the Base64 encoder / decoder is one click away.
The problem: pipes that only carry text
Computers move data through channels designed in a more innocent age. Email (SMTP) was specified for 7-bit ASCII text; anything outside a small printable set risked being rewritten, truncated or rejected by the relays it passed through. Early protocols, some terminal systems and many file formats share the same allergy: raw binary — bytes with values from 0 to 255, including control characters — cannot be trusted to survive the trip.
So a question arose: how do you send arbitrary bytes through a channel that only tolerates safe, printable characters? You re-encode the bytes using only safe characters, at some cost in size, and decode at the other end. Several encodings were invented (quoted-printable, uuencode), but the one that won is Base64, standardised in RFC 4648.
The mechanism: 3 bytes become 4 characters
Base64’s alphabet has exactly 64 characters: A–Z (values 0–25), a–z (26–51), 0–9 (52–61), + (62) and / (63). 64 is 2⁶, so each character can carry exactly 6 bits of information.
The encoder reads the input three bytes at a time. Three bytes are 24 bits; 24 splits evenly into four 6-bit groups. Each group indexes the alphabet, and out come four characters. That is the entire trick — and it explains the famous overhead: 4 characters are spent carrying 3 bytes, a fixed expansion of 33%.
When the input length is not a multiple of 3, the final group is short. One leftover byte (8 bits) yields two characters — the second padded with zero bits — plus ==. Two leftover bytes yield three characters plus =. The padding tells the decoder precisely how many real bytes the final group holds, so decoding is exact, not approximate.
Decoding reverses the process: characters become 6-bit values, values are regrouped into bytes. A decoder should reject characters outside the alphabet rather than skip them silently, because a string with characters missing is corrupted data, and corrupting it further helps no one.
The variants you will actually meet
- Standard Base64 (RFC 4648 §4): the alphabet above, with padding. Email attachments (MIME) use it, with lines wrapped at 76 characters.
- Base64URL (§5): replaces
+with-and/with_, because the standard characters mean other things in URLs and filenames; padding is usually omitted. JWTs use this variant for every segment — see what a JWT is — as do most tokens and URL-embedded identifiers. - Unpadded and streaming flavours: many APIs accept missing padding and reconstruct it, as our decoder does. A few historical variants (PEM files, bcrypt’s own alphabet) differ in details that matter if data mysteriously fails to decode.
Where it is used
- Email attachments — MIME encodes every attachment (and non-ASCII message bodies) in Base64.
- Data URIs — images and fonts inlined into HTML/CSS as
data:image/png;base64,…. - HTTP Basic authentication —
Authorization: Basic <base64(user:password)>(which is why Basic auth must only ever travel over HTTPS: Base64 hides nothing). - JSON and XML payloads — binary fields such as signatures, thumbnails and certificates travel as Base64 strings inside text formats.
- Cryptographic material — keys and certificates in PEM format are Base64 with header lines.
The mistakes
- Believing it is encryption. Base64 has no key and no secrecy; anyone can decode anything. Encode for transport; encrypt for confidentiality. If both are needed, encrypt first, then encode.
- Using it where bytes would do. Storing Base64 in a database or sending it over a binary-safe channel pays 33% for nothing. The encoding earns its cost only at a text boundary.
- Double-encoding. Encoding an already-encoded string gives
YUdWc2JHOD0=whereaGVsbG8=was meant — decodable, valid, and wrong. If decoding produces something that still looks like Base64, suspect a second layer. - Forgetting UTF-8. Text is not bytes. Encoding text as one byte per character (Latin-1 style) corrupts everything beyond ASCII. A correct tool converts text to UTF-8 bytes first — ours does — and validates UTF-8 on the way back.
Base64 endures because it is the cheapest possible bridge between the binary world computers live in and the text world their oldest protocols demand. It is not clever, not secure and not small — it is simply understood by everything, which for a transport encoding is the whole game.