What Is Base64? The Encoding, the Math, and Why It's Everywhere
Base64 explained byte by byte — the 64-character alphabet, the index table, 3-bytes-to-4-chars math, padding rules, and why it adds ~33% overhead.
Base64 is a way to write any binary data using only 64 printable ASCII characters. It exists because a lot of the internet’s plumbing — email, URLs, JSON, XML, HTTP headers — was built for text, and raw binary bytes get mangled, stripped or rejected when pushed through text-shaped pipes. Encoding bytes as boring, unambiguous characters is how a JPEG survives inside an email and a key survives inside a config file.
The alphabet and the index table
RFC 4648 §4 defines the alphabet — 26 uppercase + 26 lowercase + 10 digits + two symbols:
| Range | Characters | Index values |
|---|---|---|
A–Z |
26 | 0–25 |
a–z |
26 | 26–51 |
0–9 |
10 | 52–61 |
+ |
1 | 62 |
/ |
1 | 63 |
64 characters = 6 bits each. The 65th character you’ll see, =, is not data — it’s padding, covered below. The URL-safe variant (RFC 4648 §5, “base64url”) swaps the last two for - and _; the comparison covers when that matters.
The 3-bytes-to-4-chars math
Encoding takes the input bytes three at a time. Three bytes are 24 bits; 24 bits split evenly into four 6-bit groups; each 6-bit group indexes one alphabet character. So every 3 input bytes produce exactly 4 output characters.
Take Man — bytes 4D 61 6E:
M = 4D = 01001101 a = 61 = 01100001 n = 6E = 01101110
↓ regroup into 6-bit chunks
010011 010110 000101 101110
19 22 5 46
T W F u → "TWFu"
That’s the whole algorithm — a bit shuffle and a lookup table. Decoding runs it in reverse: four characters → 24 bits → three bytes, with = telling the decoder how many real bytes the last group held.
Padding: why outputs end in =
Input length rarely divides by 3, so the last group is short. The encoder emits however many characters the leftover bits fill (2 chars for 1 byte, 3 chars for 2 bytes) and pads the rest with = to a full quad:
| Input bytes | Leftover | Output ends | Example |
|---|---|---|---|
| n mod 3 = 0 | — | no padding | Man → TWFu |
| n mod 3 = 1 | 1 byte (8 bits → 6+2) | == |
M → TQ== |
| n mod 3 = 2 | 2 bytes (16 bits → 6+6+4) | = |
Ma → TWE= |
Two rules follow: output length is always a multiple of 4 (when padded), and a data length ≡ 1 (mod 4) can never occur — a lone character is only 6 bits, less than one byte. That second fact is how the decoder detects truncation instantly.
The 33% overhead, exactly
With padding, the output size is:
chars = 4 · ⌈n / 3⌉
For n divisible by 3 that’s exactly 4/3 — +33.3%. Rounding adds a little more: 100 bytes → 136 chars (+36%), 1 KiB (1024 B) → 1368 chars, 1 MiB → 1 398 104 chars ≈ 1.33 MB. If the transport wraps lines at 76 characters like MIME does, each CRLF pair adds 2 more characters — roughly +2.6% extra. Base64 is never smaller than its input; that’s the price of a 64-character alphabet. (Hex/base16 doubles size; base32 adds 60%.)
Where it came from and where it lives
The encoding dates to the 1993 Privacy-Enhanced Mail spec and was adopted — essentially unchanged — by MIME in RFC 2045, which also fixed the 76-column line wrap. The current canonical spec is RFC 4648 (2006). You’ll meet it today in Authorization: Basic headers (base64 of user:password), email attachments, PEM certificate blocks (-----BEGIN CERTIFICATE-----, wrapped at 64 columns), data: URLs, JWT segments (the URL-safe flavor), and anywhere JSON needs to carry bytes.
The fastest way to internalize it is to watch it happen: type Man into the encoder and you get TWFu; paste 8J+Riw== into the decoder and you get 👋 back.
Frequently asked questions
Is base64 encryption or compression?
Neither. It's a representation: the same bytes written in a restricted alphabet. Anyone can decode it — it provides zero secrecy — and the output is bigger than the input, so it's the opposite of compression. If a 'secret' is only base64'd, it's not secret.
Why exactly 64 characters?
Because 64 = 2⁶, so each character carries exactly 6 bits — and four of them carry 24 bits, which is exactly 3 input bytes. A power-of-two alphabet makes the byte↔character mapping exact with no fractional bits left over. A smaller alphabet (base32, hex) wastes more space per byte; a bigger one would have to use characters that aren't safe everywhere.
Does base64 work on binary files, or only text?
It works on any bytes — that's the point. Text goes through a charset first (UTF-8 turns 'é' into bytes C3 A9); files go through as raw bytes. The tool encodes both paths identically: bytes in, base64 out.
Is '=' part of the base64 alphabet?
No — the alphabet is exactly 64 data characters. = is a padding marker that only appears at the very end (at most twice) to round the last group up to 4 characters. A decoder that sees = mid-string is looking at corrupt input.
Where did the 33% figure come from?
3 bytes → 4 characters, so output is 4/3 of input: +33.3%. Formally it's '4 characters per 3 bytes' with = padding rounding each partial group up to a full four. MIME-style line wrapping (76-column CRLF) adds roughly another 2.6% on top.