encoding · level 7

Base32 & Base58

Alternative base encodings — when Base64 is the wrong tool.

170 XP

Base32 & Base58

Base64 is the encoding everyone learns first. It's not the only one, and for some jobs it's the wrong tool. Two siblings dominate the spaces Base64 can't reach: Base32 when humans read the result aloud and Base58 when humans copy it visually.

Why "another" base encoding

All base-N encodings answer the same question: how do you stuff arbitrary bytes into a restricted alphabet? Base64 chose 64 characters because 6 bits packs cleanly into bytes (4 chars × 6 bits = 3 bytes). It works, but the alphabet includes + and / (URL-unsafe), 0/O and I/l (visually ambiguous), and the case-sensitive alphabet doubles the chance of human error when dictating a value.

Different alphabets fix different problems.

Base32 — case-insensitive, dictation-safe

RFC 4648 defines Base32 with the alphabet:

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 2 3 4 5 6 7

Two design choices:

  • Case-insensitive. a and A decode to the same value. You can dictate a Base32 string over a phone call without spelling case.
  • No 0, 1, 8, 9. The digits that look like letters (zero/O, one/I/l) are removed. The remaining 27 are unambiguous.

Each Base32 character encodes exactly 5 bits (32 = 2⁵). Five bytes (40 bits) of input become 8 Base32 characters.

"hello"  =  68 65 6C 6C 6F  (5 bytes, 40 bits)
         =  NBSWY3DP        (8 Base32 chars)

Where Base32 shows up

  • TOTP / RFC 6238 secrets. When you scan a QR code for Authy or 1Password, the underlying secret is a Base32 string. That's why the manual-entry box always reads in capital letters and digits 2–7.
  • Tor v3 onion addresses. 56 Base32 characters of public-key material → xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.onion.
  • DNSSEC NSEC3 records. The hashed owner names are encoded in base32hex (the same alphabet idea, re-ordered).
  • GitHub two-factor recovery codes and many SSH key fingerprints.

The pattern: any value a human might re-type, dictate, or copy from a screenshot.

Base58 — visually unambiguous, no special characters

Base58 was popularised by Bitcoin. The alphabet:

123456789 ABCDEFGHJKLMNPQRSTUVWXYZ abcdefghijkmnopqrstuvwxyz

Note what's missing: 0, O (look identical in many fonts), I, l (also look identical), and every special character. What remains is exactly the characters that survive a hand-copy from paper, a phone screenshot, or a face-to-face dictation without ambiguity.

The trade-off: 58 isn't a power of 2. There's no neat "X bits per character". Encoding is done by treating the input as a big-endian integer, dividing by 58 repeatedly, and reading the remainders as alphabet indices. Each character carries log₂(58) ≈ 5.86 bits. It's slightly less efficient than Base32 (5 bits/char becomes 5.86 bits/char — more efficient actually) but slower because the algorithm is O(n²) in the input length.

[0x00, 0x01, 0x02, 0x03]  =>  "11Ldp"

Where Base58 shows up

  • Bitcoin addresses1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa is Base58Check (Base58 with a checksum).
  • IPFS CIDv0 — the older "Qm..." style content identifiers are Base58 of a multihash.
  • Solana keys — public/private keys in the Solana ecosystem.
  • Flickr photo IDs, short URL slugs, anywhere a system wants short tokens that survive being copied through tweets and SMS.

When to use which

Constraint Use
Going over the wire, want size Base64 (URL-safe variant for URLs)
Humans will read it aloud Base32
Humans will copy it visually Base58
Bytes are already hex-friendly Hex
Going inside a URL with + and = problems Base64 with URL-safe alphabet

What to watch out for

  • Base32 padding. The default alphabet pads with = to a multiple of 8 chars. Some implementations drop padding (TOTP secrets typically do, Tor onion v3 addresses don't). Strip and re-add as needed.
  • Base58 has variants. The Bitcoin/IPFS alphabet (above) is the most common. Ripple uses a slightly different ordering. Always check which alphabet a library expects.
  • Don't confuse Base32 and Base32hex. They have the same alphabet size but different orderings. Tor uses Base32; DNSSEC uses Base32hex.
  • Base58Check is not just Base58. Bitcoin addresses include a 4-byte SHA-256 checksum at the end so a typo is caught before the funds vanish. If you're building an address-like identifier, bake in a checksum.

Worked example: a TOTP secret

# Generate a 160-bit secret, encode it as Base32 — the format every authenticator app expects.
$ openssl rand 20 | base32
NBSWY3DPEB3W64TMMQQGS4DBNZRXG2A=

# Strip the padding (standard for TOTP) and feed it to oathtool to test:
$ oathtool --totp -b NBSWY3DPEB3W64TMMQQGS4DBNZRXG2A
123456

That's the full lifecycle: random bytes → Base32 string → QR code → six-digit code on a phone. Every step assumes the secret is something a human can read, which is why Base32 wins this niche.

Tools in the wild

4 tools
  • rfc4648 (npm)free tier

    Spec-faithful Base16/32/32hex/64/64url encoder for Node and browsers.

    library
  • bs58 (npm)free tier

    The canonical Base58 implementation used across the Bitcoin ecosystem.

    library
  • python-base58free tier

    Base58 / Base58Check for Python. Drop-in for Bitcoin / Solana / IPFS data.

    library
  • oathtoolfree tier

    CLI to generate / verify TOTP codes from Base32 secrets — handy for testing.

    cli