Developer

What Base32 Is, and Where You Already Use It

Five bits per character, a shrunken alphabet, and the reason your two-factor secret looks nothing like a JWT.

Two boxes holding the digits 0 and 1, each crossed out, above a row of small dots strung on a line standing for the rest of the alphabet.

Base32 turns arbitrary bytes into text using only 32 characters — the letters A through Z and the digits 2 through 7 — so the result survives being read aloud, typed on a phone keypad or copied out by hand without the two mistakes that ruin a string like that: swapping a letter for a similar-looking digit, or losing track of upper and lower case. A two-factor secret is base32 for exactly that reason, and once you know the shape of it you start noticing it in a few other places too.

How does Base32 turn bytes into letters?

Base64 packs six bits into one character, since 26 = 64. Base32 packs only five, since 25 = 32, so five bytes — 40 bits — divide evenly into eight base32 characters with nothing left over. Anything shorter than a multiple of five bytes is padded with = signs so the output length stays a multiple of eight: one leftover byte pads to six =, two leftover bytes pad to four, three pad to three, four pad to one. RFC 4648, which standardised the format in October 2006 and replaced the earlier RFC 3548, spells out exactly this table.

The narrower alphabet costs size. Base64 needs four characters for every three bytes — 1.33 characters per byte. Base32 needs eight characters for every five bytes — 1.6 characters per byte, about 20% more text for the same data. A 20-byte secret, the usual size for a TOTP key, becomes 27 characters in Base64 and 32 in Base32. You can see the Base64 half of that arithmetic directly in the Base64 encoder here: paste 20 characters of text and watch the output grow by a third rather than by three fifths.

Why does the alphabet skip 0, 1, 8 and 9?

Base32's 32 characters are the 26 letters plus six digits, and RFC 4648 picks 2 through 7 rather than any other run. That choice is not arbitrary: it removes 0 and 1, the two digits people most often misread as a letter — 0 as the letter O, 1 as the letter I or a lowercase L — while leaving every letter, including O, I and L themselves, in the alphabet. Because the digits that could be confused with them are simply never used, there is no real ambiguity: an O in a base32 string has to be the letter, since a zero never shows up there at all. 8 and 9 are left out too, though not for a confusion reason — the format only needs six digits to reach 32 symbols, and 2 through 7 is the run that was chosen.

Why is your two-factor secret Base32 instead of Base64?

A TOTP secret is normally 20 random bytes that a person has to type at least once — into a password manager, a second device or a recovery note — usually reading it off a screen. Base64's alphabet includes both cases of every letter, so a lowercase a and an uppercase A are different symbols, and getting one wrong silently changes the secret. Base32 sidesteps that: RFC 4648 states the format's goal as one that "needs to be case insensitive", so a canonical encoder emits uppercase and a well-behaved decoder treats jbswy3dp and JBSWY3DP as the same input. Paste a secret into the TOTP generator here and you can watch this decoding happen directly — spaces and lower case are accepted, and a character outside the alphabet is rejected by name rather than silently mangled.

That is also why the arithmetic behind those six-digit codes starts from a base32 string rather than a base64 one: the secret has to survive a human typing it, and base64's extra bit per character is wasted on a job done once, by voice or by hand, rather than millions of times by a machine.

Is Base32 the same everywhere?

No, and this is where the word "base32" gets slippery. RFC 4648 alone defines two different alphabets under that name:

Outside the RFC, Crockford's base32 is a third, incompatible design: it drops the letters I, L, O and U from the alphabet entirely — I and L for looking like 1, O for looking like 0, U to avoid an accidental word — and keeps the digits 0 and 1 rather than excluding them the way RFC 4648 does. Decoding is forgiving by design: a lowercase or uppercase I or L is read back as 1, and an O is read back as 0. It also defines an optional trailing check symbol for catching a single mistyped character, something RFC 4648 has no equivalent for. ULID, a sortable identifier related to UUID v7, is written in Crockford's base32 — the same 48-bit timestamp idea as UUID v7, just spelled with a different alphabet. Software built for one flavour of base32 will not necessarily read the other, so the alphabet is worth checking before assuming two "base32" strings are compatible.

Where else does Base32 show up?

Beyond two-factor secrets, three places worth recognising it:

What Base32 cannot do for you

It is not compression — five bits of alphabet per character means the encoded form is always larger than the input, never smaller. It is not encryption either: like Base64, it has no key, and anyone holding a base32 string can decode it in one step, no secret required. And it does not validate anything on its own: a corrupted base32 secret with one character swapped for another still decodes to 20 bytes, just the wrong 20 bytes, and nothing in the format itself flags that it happened. Only Crockford's optional check symbol addresses that, and RFC 4648 base32 — the kind behind your 2FA secret — has no equivalent.

The tool most directly involved in all of this is the TOTP generator: paste a base32 secret into it and it decodes the alphabet in front of you, byte count and all, the same decoding a real authenticator app does before it ever touches HMAC. Nothing is uploaded or saved — reload the page and the secret is gone.

If the six digits that come out the other end are the part you actually wanted explained, how those codes are computed from the decoded secret picks up exactly where this post stops.

Frequently asked questions

What is Base32 used for?

Mainly for values a person might have to type, read aloud or compare by eye: two-factor authentication secrets, Tor onion addresses, and sortable identifiers like ULID. Anywhere a value only ever needs to be read by a machine, Base64 is usually the better fit, since it packs more information into fewer characters.

Is Base32 case-sensitive?

The RFC that standardises it aims for a format that does not need case to be preserved, so a compliant encoder always produces uppercase and a well-behaved decoder accepts lowercase too. Not every implementation is that forgiving, so if a Base32 string is rejected, trying it in uppercase is worth checking before assuming the value itself is wrong.

Why is a Base32 string longer than the equivalent Base64 one?

Because each Base32 character carries 5 bits of information against Base64’s 6, so the same data needs about 20% more characters in Base32. A 20-byte secret comes out as 32 Base32 characters or 27 Base64 characters — the price of an alphabet built to survive being copied by hand rather than read by a parser.

What is the difference between standard Base32 and Crockford’s Base32?

Standard Base32, from RFC 4648, uses A–Z and 2–7 and keeps every letter, including the ones that resemble digits. Crockford’s Base32, used by identifiers like ULID, does the opposite: it keeps 0 and 1 but drops the letters I, L, O and U, and its decoder forgives a few common mix-ups automatically. The two alphabets are not interchangeable.

Can a Base32 string be corrected automatically if a character is mistyped?

Only if the software uses Crockford’s variant, which maps a handful of commonly mistyped characters back to the right symbol and supports an optional check character for catching the rest. RFC 4648 base32 — the kind behind a two-factor secret — has neither: a wrong character is either rejected outright or silently decoded into the wrong bytes.

Last updated September 27, 2026