Our Base58 Encoder/Decoder and Base64 Encoder/Decoder run entirely in your browser — bytes, checksums, and all — with no data leaving your machine.
Both Base58 and Base64 solve the same problem: binary data is unsafe to move through systems that expect text — emails, URLs, JSON, command lines. The difference is who the encoding is designed to serve. Base64 is built for machines moving bytes as efficiently as possible. Base58 is built for humans who must copy, read, and sometimes retype the result. That single design goal cascades into every difference between them.
1. Two Flavors of the Same Idea
Every Base-N encoding groups input bits into N-sized chunks and maps each chunk to a character from an N-character alphabet. Base64 groups 6 bits at a time (2⁶ = 64 characters). Base58 groups bits into 58-value indices (2⁵⁸ ≈ 2.88 × 10¹⁷ — an awkward, non-power-of-two boundary that requires arithmetic rather than bit-shifting). The base itself is a design decision: 64 divides evenly into byte boundaries, 58 does not.
2. Alphabets and Padding, Side by Side
| Property | Base64 | Base58 (Bitcoin) |
|---|---|---|
| Alphabet size | 64 chars: A–Z a–z 0–9 + / | 58 chars: A–Z a–z 0–9 minus 0 O I l |
| Padding | = to pad to 3-byte groups | None |
| Bits per char | Exactly 6.0 | ≈ 5.86 |
| URL-safe variant | Base64url (-_ replaces +/) | Already URL-safe |
| Ambiguous chars | 0/O, l/I/1 all present | Excluded by design |
| Line wrapping | Traditionally 76 chars (MIME) | Never |
| Designed for | Machine transport | Human transcription |
The excluded characters in Base58 are the whole point: 0/O, l/1/I are indistinguishable in most fonts. In Base64 that rarely matters — machines never misread a glyph. In a Bitcoin address where one wrong character means funds sent into a void, it matters enormously.
3. Why Bitcoin Chose Base58
Satoshi's design constraint was that a person might write an address on paper, read it aloud on a phone, or retype it into a form. Consider a Base64-encoded address containing l: is it lowercase L, digit 1, or uppercase I? Base58 eliminates the question. Beyond the alphabet:
- No
=padding — trailing equals signs look like noise and get stripped by careless form fields or text processors. - No
+or/— these require URL-escaping (%2B,%2F) in links and survive poorly through email clients that convert plain text. - Compact without wrapping — a standard address stays on one line; Base64 at 76-char MIME wrapping invites line-break corruption.
Ethereum took the opposite path (hex with EIP-55 checksum casing), which is exactly why Ethereum addresses are longer and harder to verify by eye — a contrast worth understanding alongside wallet key safety. Base58Check remains the design that best balances compactness with human error resistance.
4. The Efficiency Trade-off, Quantified
Base64 is strictly more compact for large payloads. A 32-byte payload:
- Base64: ceil(32 � 8 / 6) = 43 characters of data (+ 1 padding
=to reach a 4-character group � 44 as usually emitted). - Base58: � 44 characters on average (58 is not a power of two, so the same payload can run 44 or 45 depending on its byte values).
That is roughly 2�3% more characters � the price of human-proofing. One nuance worth knowing: on small inputs Base64's rigid 3-byte grouping can waste up to two characters on padding alone, so short payloads sometimes come out shorter in Base58 despite its lower density; section 10 works through the formulas and a length table. For machine transport at scale (JWTs, image data URLs, bulk API payloads) the premium is pure waste; the standards ecosystem already standardized on Base64 (RFC 4648) for exactly this reason. For a 34-character wallet address, the premium is irrelevant.
5. Base58Check: Where the Checksum Lives
Base58 alone has no error detection — any alphabet swap does. Bitcoin wraps it:
payload = version byte + data (e.g. 0x00 + hash160)
checksum = SHA-256(SHA-256(payload))[0..3] // first 4 bytes
encoded = Base58(payload + checksum) On decode, recompute the double-SHA-256 checksum and compare. A single character wrong anywhere in the address almost certainly breaks the match (1 in 2³² errors would slip through — negligible for typos). This is the same family of idea as credit-card modulus-10 checks: detect the error, not correct it. Nothing about Base58 enforces this — it is the convention layered on top that makes Bitcoin addresses trustworthy. Checksummed Base58 also appears outside crypto: IPFS CIDs (CIDv0) are Base58 of a multihash, and many newer systems use its cousin Base32 for case-insensitive contexts.
6. When to Use Which
| Use case | Choose | Why |
|---|---|---|
| JWT / OAuth tokens | Base64url | Standardized (RFC 7515), machine-only, native support everywhere |
| Embedding images in HTML/CSS | Base64 | data: URLs expect Base64 per spec |
| API keys, invite codes | Base58 | Human-visible, copy-safe, no = to get mangled by forms |
| Wallet addresses / public IDs | Base58Check | Typo detection before value transfer |
| HTTP Basic auth (legacy) | Base64 | RFC 7617 mandates it |
| Short links / file IDs | Base58 or base62 | Case-sensitive, readable, no special chars in URLs |
| SSH keys (new format) | Base64 | Embedded in text formats expecting RFC 4648 |
| QR-encoded payloads | Base32 or hex | Uppercase / hex alphabets fit QR alphanumeric mode, packing more data per module |
| Database surrogate keys | Base62 | Shorter than Base58 for the same entropy, URL-safe, no excluded glyphs |
| TOTP / 2FA secrets | Base32 | Case-insensitive, reads cleanly off a screen into an authenticator app |
The rule of thumb: if a human might see it, select it, or retype it, reach for Base58; if it moves only between machines, use Base64 (or Base64url in URLs).
Two adjacent contexts deserve explicit mention. For QR codes, prefer Base32 or an all-caps Base64 variant where the encoder supports it: QR alphanumeric mode packs more characters per module than byte mode, so Base32's uppercase-and-digits alphabet fits the efficient mode while Base58's mixed case forces byte mode and produces a denser, less reliably scannable code at the same payload size. For voice and paper transfer, Base58 stays the least ambiguous of the dense alphabets � but pair it with a checksum as in section 5, because the threat model is transcription error, not compression.
7. Conversion Mistakes That Corrupt Data
- Character-level "conversion." Base64→Base58 is not a remapping — decode to bytes first, then encode. Bit groupings (6 vs 5.86) make direct substitution produce garbage.
- Ignoring line breaks. Base64 from PEM/MIME files carries newlines; feeding them through as data shifts every byte. Strip whitespace before decoding.
- Standard vs URL-safe Base64.
+/vs-_— decoding with the wrong table fails or silently misreads. (Our Base64 Encoder auto-detects both variants.) - Padding handling. Some decoders reject missing
=; others require it. Trim or normalize consistently. - Dropping the checksum. Re-encoding a Bitcoin-style address without recomputing the 4-byte checksum yields an address that decodes fine and is still wrong. Always recompute on round-trip.
- Scope confusion. Base16 (hex), Base32, Base62, and Bech32 each have distinct alphabets — "just use Base58" in code review means exactly 58 characters with those four exclusions, nothing else.
- Treating Base58 as encryption or hashing. It is neither: the transformation is fully reversible with no key, so anyone holding the string holds the bytes. Encode secrets only for transport; real protection comes from hashing (passwords) or authenticated encryption (payloads).
- Assuming case-insensitive handling. Base58, Base62, and Base64 are all case-sensitive; lowercasing a Base58 key silently changes its value. Systems that must survive case-folding � DNS, some ticketing and logging pipelines � need Base32 instead.
Whenever a conversion feels suspicious — addresses growing, bytes not matching — verify with two independent tools and compare intermediate byte output, not final strings. For pure byte-level work, pair the two encoders and compare intermediate byte output with a hex inspector; and for the JSON/API side of things where Base64 appears inside payloads, the JSON Formatter keeps the envelope readable while you inspect it.
8. Base58Check Worked Example, Step by Step
Follow one real payload all the way through so the layers stop being abstract. Take the body of a legacy Bitcoin mainnet address: a version byte (0x00) followed by a 20-byte hash160 � 21 bytes total:
payload (21 bytes):
00 62 e9 07 b1 5c bf 27 d5 42 53 99
eb f6 f0 fb 50 eb b8 8f 18
step 1 � double SHA-256 of the payload:
hash = SHA256(SHA256(payload))
step 2 � take the first 4 bytes as the checksum:
checksum = hash[0..3] -> c2 9b 7d 93
step 3 � append checksum (25 bytes total), Base58-encode:
encoded = Base58(payload ++ checksum)
= 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa On the way back, a decoder Base58-decodes the string, splits off the last four bytes, recomputes the double-SHA-256 over the remaining 21 bytes, and compares. A mismatch rejects the value before any wallet logic runs � change one character of that address and the recomputed checksum will not match the embedded one. You can reproduce every step yourself: the Hash Generator computes the SHA-256 pairs in isolation, and the Base58 Encoder/Decoder handles the final step. The ordering is the part people get wrong: checksum the bytes, then encode the combined bytes; never encode first and append Base58 text.
9. The Base Family: Base32, Base62, Bech32 and Neighbors
Base58 sits inside a larger design space where each member trades compactness against human factors differently:
| Encoding | Alphabet | Padding | Case-sensitive? | Where you meet it |
|---|---|---|---|---|
| Base16 (hex) | 16 chars, 0�9 a�f | No | Conventionally lowercase | Hashes, byte dumps, Ethereum addresses (with EIP-55 checksum casing) |
| Base32 (RFC 4648) | 32 chars, A�Z + 2�7 | = padding | No � designed to be case-insensitive | TOTP secrets, file transfer, Crockford's variant in IDs |
| Base58 (Bitcoin) | 58 chars, minus 0 O I l | No | Yes | Bitcoin legacy addresses, IPFS CIDv0, human-visible API keys |
| Base62 | 62 chars, 0�9 A�Z a�z | No | Yes | Short URLs, database keys, video IDs |
| Base64 (RFC 4648) | 64 chars + = | = to 4-char groups | Yes | PEM, data: URLs, HTTP Basic, general transport |
| Base64url (RFC 4648 �5) | 64 chars, -_ instead of +/ | Optional � RFC 7515 omits it | Yes | JWTs, SAML, web API payloads |
| Bech32 / Bech32m (BIP-173/350) | 32 chars + 1 separator, constant checksum | No | No � all lowercase | SegWit and Taproot Bitcoin addresses (bc1q...) |
Two details pay off in reviews. Base32 sacrifices density (5 bits per character) for case-insensitivity, which is why authenticator apps can read TOTP secrets off a screen without ambiguity about glyph case. Bech32 spends its error budget on a stronger checksum than Base58Check and guarantees an all-lowercase, space-tolerant format designed for visual transfer � the 1 before the data part acts as an unambiguous separator, so bc1 prefixes are greppable in logs and the lowercase-only rule blunts the homoglyph tricks that mixed-case alphabets invite.
10. Length Math: Formulas You Can Check by Hand
For Base64, length is a pure function of input size. Given n input bytes:
- Characters: 4 � ceil(n / 3), padding included.
- Padding bytes: (3 - (n mod 3)) mod 3 equals signs.
- Overhead: worst case +2 characters on a 3-byte group � under 34% worst-case, about 25% on average for random data.
For Base58 there is no fixed ratio because 58 is not a power of two. The bounds:
- Characters: between floor(n � log(256) / log(58)) and that value plus one � plus one extra
1character per leading zero byte of input. - Bits per character: log2(58) � 5.858, so expect roughly n � 8 / 5.858 characters for random data.
- Leading zeros: each 0x00 input byte becomes one
1� the reason Base58 strings starting with 1s map to zero-prefixed payloads.
| Input bytes | Base64 chars (padded) | Base58 chars (typical) | What happens |
|---|---|---|---|
| 16 | 24 | 22�23 | Base58 wins � Base64 wastes a whole padding group |
| 20 | 28 | 27�28 | Parity zone |
| 32 | 44 | 44�45 | Roughly even |
| 64 | 88 | 88�89 | Base64 pulls ahead |
| 256 | 344 | 348�349 | Base58 about 1.5% longer |
The intuition worth remembering: Base64's fixed 3?4 grouping wastes up to a whole group on small inputs, so short payloads can come out shorter in Base58 despite its lower density. For long random payloads density dominates and Base64 wins by roughly 2�3%.
11. Implementations in JavaScript and Python
Base64 needs no library in either language. Base58 does � the big-integer division is too easy to get subtly wrong for a one-off:
// JavaScript: Base64 is built in; Base58 comes from a vetted package
const b64 = Buffer.from(bytes).toString("base64"); // standard, with padding
const b64url = Buffer.from(bytes).toString("base64url"); // URL-safe, no padding
// Base58 (npm: bs58) � Uint8Array in, string out
import bs58 from "bs58";
const encoded = bs58.encode(bytes);
const decoded = bs58.decode(encoded); // Uint8Array # Python: base64 is standard library; Base58 is a small dependency
import base64
b64 = base64.b64encode(data).decode() # standard
b64url = base64.urlsafe_b64encode(data).decode() # URL-safe, padding kept
# pip install base58
import base58
encoded = base58.b58encode(data).decode()
decoded = base58.b58decode(encoded) Three implementation pitfalls account for most round-trip bugs:
- Encoding strings instead of bytes. JavaScript's
Buffer.from(str)coerces via UTF-8, while Python'sstrneeds an explicitencode("utf-8")first. Decide whether your unit of data is bytes or text and convert once at the boundary. - Forgetting that Base58 has no checksum. A library named
bs58gives you the alphabet, not Base58Check. Bitcoin-style verification is extra code on top � implement it, or use a library that already does (bs58check). - Trusting a hand-rolled implementation. Off-by-one errors in the division loop surface as "works for short inputs, corrupts long ones." Use a maintained package, then add a round-trip property test over random byte strings of lengths 0 through 64.
When the job is converting text that currently holds Base64 into Base58, decode to bytes once, verify those bytes against a hex dump, then encode � comparing intermediate hex, not final strings, is what catches mistakes early. On the adjacent API side, our API Key Generator emits high-entropy identifiers, and the pattern of choice for human-visible keys is exactly the unambiguous, padding-free alphabet this article describes; the JSON Formatter keeps the surrounding payload readable while you inspect where an encoding appears inside it.
Conclusion
Base58 and Base64 are not competitors — they are two answers to different questions. Base64 asks: how do I move these bytes through text systems with minimum overhead? Base58 asks: how do I present these bytes so a human cannot plausibly destroy them by looking at them? Knowing which question your data is answering tells you which encoding to reach for.
Frequently Asked Questions
- What is the difference between Base58 and Base64?
- Base64 uses 64 characters (A–Z, a–z, 0–9, +, /) plus '=' padding for transport efficiency. Base58 uses 58 characters with no padding, excluding 0, O, I, and l to prevent transcription errors — it backs Bitcoin addresses and human-facing identifiers.
- Why does Bitcoin use Base58 instead of Base64?
- Bitcoin addresses are typed and read by humans. Base58 removes visually ambiguous characters so users don't mistype funds, and drops padding that email clients and form fields would mangle. Base64's +, /, and = are liability in manual entry.
- Which is more efficient?
- Base64: exactly 6 bits per character with clean 3-byte→4-character grouping. Base58 packs ≈5.86 bits per character, so identical data takes roughly 2–3% more characters — the cost of human-proofing.
- Does Base58 have checksum protection?
- Base58 itself is only an alphabet. Bitcoin's Base58Check variant appends a 4-byte double-SHA-256 checksum before encoding, so any typo produces a value wallets reject immediately.
- Is Base58 safe for API tokens?
- Yes — it's URL-safe, padding-free, and unambiguous, ideal for API keys and invite codes. For pure machine-to-machine transport, Base64url remains the standard and is slightly more compact.
- What is Base58Check?
- version byte + payload + 4-byte SHA-256d checksum, encoded in Base58. Decoders recompute and verify the checksum, catching typos before any transaction.
- Can I convert Base64 to Base58 directly?
- Not by character substitution — alphabets and bit groupings differ. Decode Base64 to raw bytes, then encode those bytes as Base58. Skipping the byte step yields garbage.
- When should I use Base64url instead of Base58?
- For JWTs, data URLs, and web-standard payloads: Base64url swaps +/ for -_ so it survives URLs unescaped, with native support in every framework. Reserve Base58 for human-facing identifiers.