The word Man becomes TWFu. Three characters in, four characters out โ and that 4-for-3 ratio is the entire story of Base64. Everything you feed it comes back about 33% bigger, and it comes back as plain, boring text that any system can carry without choking.
Here's exactly how Man turns into TWFu, because once you see it the rest is easy.
How three bytes become four characters
Take the letters Man. In ASCII their byte values are M=77, a=97, n=110. Written in binary that's:
01001101 01100001 01101110
That's 24 bits. Base64 ignores the original 8-bit byte boundaries and re-slices those same 24 bits into four groups of 6 bits each:
010011 010110 000101 101110
19 22 5 46
Now each of those numbers (0 through 63) picks a character from a fixed 64-slot alphabet: AโZ holds 0โ25, aโz holds 26โ51, 0โ9 holds 52โ61, then + is 62 and / is 63. So 19โT, 22โW, 5โF, 46โu. Out comes TWFu.
That's the whole trick. Base64 doesn't understand what your data means. It just regroups the raw bits from chunks of 8 into chunks of 6, and every possible 6-bit value has a printable character waiting for it. Since 6 bits can't produce anything outside those 64 safe characters, the output is guaranteed to survive systems that only trust plain text.
What happens when the length doesn't divide by three
Three bytes map cleanly to four characters, but most data isn't a tidy multiple of three. That's what the = signs are for.
Encode just M (one byte, 8 bits). You pad it out to fill 6-bit groups and get TQ==. Encode Ma (two bytes, 16 bits) and you get TWE=. The = characters aren't data โ they're padding that tells the decoder "the last group was short, ignore the filler bits." One = means the final group held two real bytes; two = means it held one. A decoder uses those signs to reconstruct the exact original, byte for byte. Base64 is fully reversible; nothing is lost.
Where you actually run into it
You've almost certainly seen Base64 without registering it:
- Data URIs. A tiny icon embedded directly in HTML or CSS as
data:image/png;base64,iVBORw0KGgo...is a Base64-encoded image. The browser decodes it back into pixels. This saves an HTTP request for small assets. - Email attachments. Email was built to carry text. A PDF or JPEG is binary, so MIME Base64-encodes it into text, ships it, and the receiving client decodes it. That photo you attached rode across the internet as a wall of letters and digits.
- JWT tokens. The auth tokens in
header.payload.signatureform are Base64-encoded (a URL-safe variant, more on that below). The header and payload decode straight to plain, readable JSON โ not gibberish. - Config files and API responses. Binary keys, certificates, or small blobs get Base64'd so they fit inside JSON, YAML, or environment variables that expect strings.
The common thread: some pipe only carries text, and you need to push bytes through it intact.
If you want to watch the transformation happen in both directions, paste any string or file into the Base64 Encode/Decode tool. It runs entirely in your browser โ nothing you paste is uploaded to a server, which matters when the thing you're inspecting is a token or a config value you'd rather not send anywhere.
The 33% tax is real, and sometimes it stings
Four output characters for every three input bytes means Base64 output is roughly 133% the size of the original. A 100 KB image becomes about 133 KB of text. A 3 MB attachment travels as roughly 4 MB.
For a 1 KB icon inlined into a stylesheet, nobody notices. For large files it adds up: a mail server enforcing a 25 MB attachment limit is really capping you around 18โ19 MB of actual file, because the encoding inflates the rest. When people are surprised that a "20 MB" file gets rejected by a 25 MB limit, this overhead is usually why. Base64 is the right call for compatibility, not for saving bandwidth โ it costs bandwidth by design.
It is not encryption โ this is the part people get wrong
The one thing to burn into memory: Base64 hides nothing. There's no key, no password, no secret. The alphabet is public and the algorithm is trivial. Anyone who sees cGFzc3dvcmQxMjM= can decode it to password123 in one second with any decoder on earth, including the one linked above.
Encoding is about format โ making data survive transport. Encryption is about secrecy โ making data unreadable without a key. Base64 does the first and none of the second.
This trips up more people than it should. A JWT payload looks scrambled, so someone stuffs a user's role or email into it and assumes it's private. It isn't โ paste the middle segment into a decoder and read it plainly. That's fine for data that's meant to be readable (JWTs are signed to prevent tampering, not to hide contents), but it's a genuine security bug when someone Base64-encodes an API key or password and believes they've protected it. If it must stay secret, it needs real encryption or a secrets manager. Base64 is an envelope, not a lock.
One footnote you'll hit in the wild: the standard alphabet's + and / cause problems in URLs and filenames, so a Base64url variant swaps them for - and _ and often drops the = padding. JWTs use this. It's the same idea with two characters remapped.
When Base64 isn't the right tool
- You want secrecy. Use encryption. Base64 provides zero confidentiality.
- You want to shrink data. It grows data by a third. Compress first (gzip, or an image codec), then encode only if a text-only pipe forces you to.
- You're moving large files through a channel that already handles binary. If the transport is binary-safe, encoding just wastes 33% for nothing.
- You need integrity or authenticity. Base64 won't tell you if data was altered. That's what checksums and signatures are for.
The honest summary: Base64 is a translation layer that lets arbitrary bytes travel through text-only systems, reversibly and losslessly, at a predictable 33% size cost โ and it offers no privacy whatsoever. If you ever catch yourself using it to "hide" a value, stop; you've reached for a mailing envelope when you needed a safe.