Base32 vs Base64: Which Encoding Should You Use?
Base32 vs Base64: Which Encoding Should You Use?
Both Base32 and Base64 are binary-to-text encodings defined in the same RFC 4648 family. They let you ship raw bytes through channels that only accept letters and digits — email, URLs, DNS records, and configuration files. They share a goal but make opposite trade-offs: Base64 packs data as tightly as a 64-symbol alphabet allows, while Base32 trades density for case-insensitivity and freedom from confusing digits. When you need a standard, compact transform, the Base64 Encoder / Decoder is what every stack already understands; when the value will be read or typed by a human and case could be mangled, reach for the Base32 Encoder / Decoder.
This comparison walks through how each encoding works, how their alphabets, case handling, and padding differ, the mistakes that bite developers, and real tested outputs from the live tools so you can predict exactly what you will get.
What Is Base64?
Base64 maps every 3 input bytes (24 bits) onto 4 output characters drawn from a
64-character alphabet: A–Z, a–z, 0–9, plus + and /. Because 24 is not
always divisible by the input length, it pads the tail with = characters so the
output length is always a multiple of four. It is the default binary-to-text
encoding on the web: MIME email attachments, data: URLs, HTTP Basic Auth
headers, and JWT signatures all lean on it.
The design goal is lossless density inside any text channel, even one that mangles
whitespace or high bytes. Human readability is explicitly not a concern — and
neither is case safety, because Base64 is case-sensitive: A and a decode to
different values.
What Is Base32?
Base32 takes the same "bytes to a larger alphabet" idea but uses only 32 symbols:
A–Z plus the digits 2–7. It deliberately omits 0, 1, 8, and 9, which
are the digits most easily confused with letters. Every 5 bits of input become one
output character, and the tail is padded to a multiple of eight with = characters
(up to six of them).
The killer property of Base32 is that it is case-insensitive: a decoder treats
a and A identically. That makes it safe to hand around in environments where
case gets lowercased or mangled — think DNS TXT records, provisioning QR codes, or
TOTP shared secrets (Google Authenticator and RFC 6238 seed strings are Base32
precisely for this reason).
Base32 vs Base64: A Side-by-Side Comparison
| Dimension | Base64 | Base32 |
|---|---|---|
| Alphabet size | 64 | 32 |
| Symbols used | A–Z a–z 0–9 + / | A–Z 2–7 |
| Confusing digits omitted | No (0 1 8 9 present) | Yes (omits 0 1 8 9) |
| Case sensitive? | Yes | No (decodes case-insensitively) |
| Padding | = up to 2 chars | = up to 6 chars |
| Output length factor | ~1.33× input bytes | ~1.60× input bytes |
| Standardized | RFC 4648 | RFC 4648 |
| Native language support | Yes (btoa/atob, base64) | Partial (base64.b32encode) |
The headline takeaway: Base64 wins on density and ubiquity; Base32 wins on case-safety and digit clarity. Pick Base64 when a machine reads the result, and Base32 when a human or a case-mangling channel is in the loop.
If you have ever scanned a 2FA setup code, the secret behind it was Base32. The missing
0/1/8/9and the case-insensitivity are exactly why those seeds survive being typed, lowercased, or read aloud without breaking.
Alphabet, Case, and Padding
Base64's alphabet is ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/.
Base32's alphabet is the 32 symbols ABCDEFGHIJKLMNOPQRSTUVWXYZ234567.
Case is the sharpest practical difference. Base32 decoders uppercase the input
first, so nfxi... and NFXI... decode to the same bytes. Base64 decoders do no
such thing — aGVsbG8= and AGVsbG8= are completely different payloads. If your
transport lowercases everything (some SMS gateways, older DNS tooling), only
Base32 survives intact.
Padding also differs in volume. Base64 needs at most two = characters; Base32 can
need up to six because it groups bits in fives rather than sixes. The padding is
cosmetic for the encoder but mandatory for a correct decode, since it marks how
many trailing bits were zero-filled.
Output Size and Use Cases
Because Base32 has half as many symbols as Base64, it expands data roughly 1.6× versus Base64's 1.33×. For a 100 KB blob, that difference is ~27 KB — usually irrelevant for secrets and identifiers, occasionally painful for bulk payloads.
Where Base32 earns its keep:
- TOTP / 2FA seeds (RFC 6238) — case-insensitive and digit-clean.
- DNS challenges and TXT records — case can be normalized upstream.
- Provisioning QR codes — scanned, then typed or pasted by humans.
- File identifiers in case-insensitive filesystems — no
avsAambiguity.
Where Base64 stays king: HTTP bodies, data URLs, email, and any place a library
already hands you btoa/atob for free.
Common Mistakes
- Assuming Base32 is just "Base64 with fewer chars" — the grouping is 5 bits per character, not 6, so the output length and padding rules diverge. Reuse a Base64 routine for Base32 and you get garbage.
- Lowercasing a Base64 string — because Base64 is case-sensitive, folding case corrupts the payload. Use Base32 if case may change in transit.
- Dropping the
=padding — Base32 can carry up to six pad characters; stripping them breaks a strict decoder's length expectation. - Confusing Base32 with Base32Hex — RFC 4648 also defines "Base32Hex" using
0–9andA–V. They are not interchangeable; match the alphabet exactly. - Hand-editing a pasted value — a single typo silently corrupts the result. Use the Base32 Encoder / Decoder or the Base64 Encoder / Decoder to transform and verify instead of editing by hand.
Code Examples
JavaScript
// Base64 (standard, built into the browser / Node)
const toBase64 = (s) => btoa(unescape(encodeURIComponent(s)));
const fromBase64 = (b) => decodeURIComponent(escape(atob(b)));
// Base32 (same logic as the CodeToolPro tool, RFC 4648)
const B32 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
function toBase32(str) {
const bytes = new TextEncoder().encode(str);
let bits = "";
for (const byte of bytes) bits += byte.toString(2).padStart(8, "0");
let out = "";
for (let i = 0; i < bits.length; i += 5) {
out += B32[parseInt(bits.substring(i, i + 5).padEnd(5, "0"), 2)];
}
return out + "=".repeat((8 - (out.length % 8)) % 8);
}
console.log(toBase64("internet")); // aW50ZXJuZXQ=
console.log(toBase32("internet")); // NFXHIZLSNZSXI===
Python
import base64
B32 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
def to_base32(s: str) -> str:
data = s.encode()
bits = "".join(f"{b:08b}" for b in data)
out = "".join(B32[int(bits[i:i+5].ljust(5, "0"), 2)]
for i in range(0, len(bits), 5))
return out + "=" * ((8 - len(out) % 8) % 8)
print(base64.b64encode(b"internet").decode()) # aW50ZXJuZXQ=
print(to_base32("internet")) # NFXHIZLSNZSXI===
Both snippets reproduce the CodeToolPro tool output exactly, so results are portable across languages.
Hands-on: Tested with the Tool
I ran the same inputs through both live tools to confirm the documented behavior.
- Open the Base32 Encoder / Decoder and type
internetin Encode mode. The output isNFXHIZLSNZSXI===(16 characters, three=pads). The alphabet contains only uppercase letters and digits 2–7 — no0,1,8, or9appears anywhere. - Switch to Decode and paste
NFXHIZLSNZSXI===back: the tool returnsinternet, confirming a clean round-trip with no data loss. - Open the Base64 Encoder / Decoder. Encoding
internetyieldsaW50ZXJuZXQ=(12 characters, one=pad) — shorter than the Base32 form, exactly as the density table predicts. Decoding it returnsinternet. - For a punctuation-heavy sample I encoded
Hello, World!:- Base32 →
JBSWY3DPFQQFO33SNRSCC===(24 chars) - Base64 →
SGVsbG8sIFdvcmxkIQ==(20 chars) Both decoded back toHello, World!without error.
- Base32 →
- A single character shows the padding gap most starkly:
fbecomesMY======in Base32 (six pads) butZg==in Base64 (two pads) — same byte, very different padding volume. - Case test: decoding
nfxhizlsnzsxi===(all lowercase) in the Base32 tool still returnsinternet, proving the decoder is case-insensitive. The same lowercase trick on a Base64 value would fail or corrupt, because Base64 is case-sensitive.
Observed rule: for equal input, Base32 was always longer than Base64 (the 32 vs 64 alphabet costs about 20% density), but its outputs survived a lowercase round-trip that Base64 could not. The decisive difference was case-safety, not length.
Related Tools
- Encode and decode standard text with the Base64 Encoder / Decoder.
- Convert bytes without confusing digits or case using the Base32 Encoder / Decoder.
- Explore the Bitcoin-friendly sibling with the Base58 Encoder / Decoder.
- Convert between hex and text using the Hex to ASCII Converter.
- Read the single-tool deep dive in the Base64 Guide or the Base58 vs Base64 comparison.
When to Use a Tool Instead of Code
You rarely need a tool to apply Base64 — btoa/atob or your language's
base64 module is one line, and Python even ships base64.b32encode for Base32.
The tools earn their place when you are inspecting or translating data you did
not author: a TOTP secret a vendor emailed you, a value you must sanity-check
before trusting it, or a string you want to round-trip without writing a scratch
script. The Base32 Encoder / Decoder is especially
handy because Base32's case-insensitivity and padding are easy to get subtly wrong
by hand.
For production code, keep the encoding in your application runtime and use the tools to debug; for ad-hoc lookups, verification, and human-facing identifiers, the browser tools are faster and safer than spinning up a script — and nothing leaves your machine.