Skip to main content

Base32 vs Base58: Picking the Right Alphabet for Secrets, Addresses, and Short Codes

A practical guide to Base32 and Base58 encoding — why Base32 fits TOTP secrets and DNS, why Base58 suits crypto addresses, and how both compare to Base64.

Published By Li Lei
#base32 #base58 #encoding #totp #bitcoin

Base32 vs Base58: Picking the Right Alphabet for Secrets, Addresses, and Short Codes

Most developers reach for Base64 by reflex. It is the default in almost every standard library, and for shoving binary data into a JSON field it is perfectly fine. But Base64 carries two characters — + and / — that have to be percent-escaped in URLs, plus = padding that breaks copy-paste, and it is case-sensitive. The moment your encoded bytes need to survive a phone call, a handwritten coupon, a DNS label, or a crypto wallet, Base64's alphabet starts working against you.

Base32 and Base58 exist for exactly those moments. They are not "Base64 with extra steps" — they make deliberate trades. Base32 sacrifices density for a case-insensitive, punctuation-free alphabet. Base58 sacrifices a little more density to drop the characters humans misread. This guide walks through where each one earns its keep, with a worked example you can reproduce in seconds using the Base32 / Base58 encoder.

What Base32 actually is, and why it wins for TOTP

Base32 packs your bytes 5 bits at a time into a 32-character alphabet. The standard RFC 4648 variant uses A-Z and 2-7 — note it skips 0, 1, 8, and 9 to keep the letters and digits from colliding visually. Because every character is uppercase or a digit, the encoding is effectively case-insensitive on decode, and it contains zero punctuation.

That property is why Base32 is the format behind every TOTP secret you have ever scanned. When you set up two-factor auth and the app shows a string like JBSWY3DPEHPK3PXP, that is RFC 4648 Base32. Google Authenticator, Authy, and the otpauth:// URI scheme all expect it, precisely because a 2FA secret sometimes has to be typed by hand from a recovery sheet or read aloud during account recovery. No +/= to garble, no case to get wrong.

The same logic explains why Base32 shows up in DNS-based encoding (where labels are case-insensitive and limited to letters and digits) and in NSEC3 hashes. If your channel is restrictive about characters and forgiving about length, Base32 is the safe bet. It also has a sortable cousin — base32hex (0-9 A-V) — whose encoded strings sort in the same order as the raw bytes, which is why ULID-style identifiers and some database row IDs use it.

What Base58 actually is, and why crypto adopted it

Base58 takes a different route. Instead of fixed-width bit packing, it treats your bytes as one big number and repeatedly divides by 58 (the encoder uses BigInt long-division under the hood). The 58-character alphabet is the interesting part: it starts from 0-9 A-Z a-z and then deletes the four characters people most often confuse — 0 (zero), O (capital o), I (capital i), and l (lowercase L).

Drop those look-alikes and you get a string a human can read off a screen, type into a wallet, or check digit-by-digit without second-guessing whether that character is a zero or an O. That is the whole reason Satoshi chose it for Bitcoin addresses in 2009, and why Solana public keys, IPFS CIDv0 hashes (Qm...), and countless URL shorteners use Base58 today. The cost is density: because 58 is not a power of two, the math is messier and the output runs slightly longer than Base64 — roughly 1.37 characters per byte versus Base64's 1.33.

One subtlety trips people up constantly: Base58 preserves leading zero bytes by prepending the alphabet's first character. A Bitcoin mainnet address begins with 1 because the version byte is 0x00, and pure division would otherwise drop that leading zero. A correct encoder counts the zero bytes first and prepends one 1 per byte. Get that wrong and your addresses silently shorten and break.

A worked example: the same six bytes, three ways

Take the input Hello — the five ASCII bytes 48 65 6c 6c 6f in hex. Paste them in hex mode and turn on "Compare all 5", or just encode each way:

| Encoding | Output | Chars | | --- | --- | --- | | Hex | 48656c6c6f | 10 | | Base64 | SGVsbG8= | 8 | | Base32 (RFC 4648) | JBSWY3DP | 8 | | Base58 (Bitcoin) | 9Ajdvzr | 7 |

For five bytes the differences look small, but the personality of each alphabet is already visible. Base64 carries a trailing =. Base32 is all uppercase letters and digits — read it aloud and nobody mishears. Base58 is the shortest here and contains no confusable characters. Now imagine 32-byte tokens instead: hex hits 64 characters, Base32 lands around 52, Base64 around 43, and Base58 around 44. For a QR code in alphanumeric mode (uppercase letters and digits only), Base32 fits the restricted character set; for byte-mode QR or a human-typed code, Base58 stays compact and unambiguous.

How they stack up against Base64

It helps to line up the trade-offs side by side:

  • Density: Base64 (1.33 chars/byte) ≈ Base58 (1.37) < Base32 (1.60) < Hex (2.00). Base64 is the most compact, Base32 the chunkiest.
  • URL safety: Base58 and Base32 are clean ASCII letters and digits with no escaping. Standard Base64 needs +, /, and = percent-encoded (the URL-safe Base64 variant fixes this, but not the case-sensitivity).
  • Human transcription: Base58 wins — no look-alikes at all. Base32 is case-insensitive but keeps some ambiguous-ish pairs. Base64 is the worst to dictate.
  • Sortability: base32hex preserves byte order in text sorts. Base58's number-based math scrambles it.

If you only remember one rule: use Base32 when a human has to read or type the value into a system that is picky about characters but relaxed about length (TOTP, DNS, recovery codes). Use Base58 when a human has to read or type a value where every character counts and mistakes are expensive (crypto addresses, short URLs, public keys). Reach for Base64 when the value lives entirely inside machines — API payloads, data URIs, email attachments — and compactness matters more than readability.

How I actually use this day to day

When I am auditing a wallet integration, I keep the Base32 / Base58 encoder open in a tab as a sanity check. The move I make most often is decoding a Bitcoin address back to its underlying hash: paste the address, switch to decode with the Bitcoin alphabet, set output to hex, and confirm the first byte is 00 for a mainnet P2PKH address. The other habit I have picked up is generating TOTP secrets by encoding random bytes to RFC 4648 Base32, then re-encoding the same bytes to Crockford Base32 to get a dictate-friendly recovery code with no I/L/O/U. Same secret, two representations, and I never have to second-guess whether the receiving app supports the variant — I only ship the RFC 4648 form into anything that matters.

The thing I want to leave you with is that none of these encodings are interchangeable, even when they look similar. A Bitcoin address decoded with the Ripple alphabet does not error — it produces silent garbage. A Crockford-encoded secret handed to a TOTP app that wants RFC 4648 simply fails. Knowing which alphabet a string came from is half the job; the encoder just does the arithmetic.


Made by Toolora · Updated 2026-06-13