Understanding Base32
The encoding for things humans have to read aloud.
A 32-letter alphabet that fits on a keypad, survives a phone call, and never confuses a zero with an O.
What it is.
Base32 is a binary-to-text encoding from the same RFC 4648 family as Base64, but with a smaller alphabet — 32 characters instead of 64. The standard alphabet is the uppercase A–Z plus 2–7 (the digits 0, 1, 8, 9 are deliberately omitted because they're easy to confuse with letters). The padding character is the equals sign, same as Base64. Bytes go in, ASCII-readable text comes out, 1.6× longer than the original.
A–Z · 2–7 (= for padding)
Why 32 letters.
Thirty-two is two to the fifth — five bits per character. Bytes come in eights. The least common multiple is forty bits — five bytes, eight Base32 characters. That's the repeating block. Compare with Base64: six bits per character, 24-bit block, three bytes to four characters. Base32 pays for its smaller alphabet with a worse ratio: an 8-to-5 inflation, not 4-to-3. Five bytes in, eight characters out — payload grows by exactly 60 %.
5 bytes ⇒ 8 characters (+60 %)
Why the smaller alphabet wins anyway.
Base64 wins when the only thing you care about is fitting binary through a text channel. Base32 wins everywhere the encoded string has to be handled by a human eyeball — read aloud on a phone call, typed into a wireless authenticator app, transcribed off a printed sticker, dictated to a tech-support agent. Lowercase looks identical to uppercase on most printouts; the digit 1 looks like a capital I; the digit 0 looks like a capital O. Base32's alphabet drops every one of those ambiguous pairs. Type it in any case and any decoder will accept it.
A worked encoding.
Take the three-character string "foo". Its bytes are 102, 111, 111 — binary 01100110 01101111 01101111. Slice that 24-bit stream into five-bit groups: 01100 11001 10111 10110 1111 — the last group has only four bits, so we pad with one zero bit to make a five-bit group 11110. We now have five five-bit groups; the block wants eight to be complete, so three more pad-positions get rendered as =. Look up each five-bit group in the alphabet (12 → M, 25 → Z, 23 → X, 22 → W, 30 → 6) and append three padding signs. The result is MZXW6===.
"foo" to Base32
ASCII: 102 111 111 → 24 bits → 5-bit groups
Three bytes is a partial block; three '=' pad out the missing groups.
"foo" → MZXW6===
= MZXW6===
"Hello!" to Base32
6 bytes → 48 bits → 10 groups (last 4 bits padded)
Six bytes is one full 5-byte block plus one byte over.
"Hello!" → JBSWY3DPEE======
= JBSWY3DPEE======
The TOTP connection.
Almost every time-based one-time-password app — Google Authenticator, Authy, 1Password's built-in OTP — provisions its secret as Base32. RFC 6238 doesn't mandate the encoding directly, but the de-facto industry secret format is the otpauth:// URI scheme defined by Google, and that URI specifies the secret as Base32. The reason is exactly the human-readable property above: a user might have to manually type the secret if QR scanning fails. Base32 is the encoding choice that survives manual entry on a phone keyboard.
Variants worth knowing.
RFC 4648 also defines "base32hex" — extended hex — which keeps the property that sort-order matches the underlying byte order. Its alphabet is 0–9 then A–V; useful when encoded strings are sorted lexically and you want that ordering to mean something. Crockford's Base32, a separate proposal, drops I, L, O, U from the alphabet to eliminate visual ambiguity and the rude-word risk; it's the encoding behind ULIDs and various short-link schemes. The original RFC 3548/4648 alphabet is what almost every authenticator and almost every standard uses by default.
When not to use it.
Use Base64 when the encoded payload only lives inside machine-to-machine plumbing — JWT bodies, data URLs, JSON columns. The 33 % inflation is smaller than Base32's 60 %, and humans never see the string. Use hex when you want byte-exact alignment and a one-to-one mapping that even non-programmers can compare visually. Use Base32 when a human is going to lay eyes on the string and you can't predict the medium it'll be carried across.