Base64 Encoding Explained: Why It's Not Encryption
Base64 is so common in 2026 web development that few developers stop to understand what it actually does. Used incorrectly, it bloats payload sizes, breaks parsers, or — most embarrassingly — gets confused with encryption. Here is the complete primer.
What Base64 actually is
Base64 is a binary-to-text encoding that represents binary data using 64 ASCII characters: A-Z, a-z, 0-9, +, /. Every 3 bytes of binary become 4 characters of Base64. There's also = as a padding character.
The "64" comes from 2^6 = 64 — each Base64 character encodes 6 bits.
What Base64 is NOT
- Not encryption. Anyone can decode Base64 with a single command. Don't use it to "hide" passwords or API keys.
- Not compression. Base64 makes data 33% LARGER, not smaller.
- Not URL-safe by default. The
+and/characters break URLs. Use Base64URL (replaces + with -, / with _) for URLs. - Not a hash. It's reversible. A hash is one-way.
How encoding works (the mechanic)
Take 3 bytes (24 bits): "abc" = 01100001 01100010 01100011
Split into 4 groups of 6 bits: 011000 010110 001001 100011
Map each 6-bit group to a Base64 character: Y W J j
So "abc" in Base64 is "YWJj".
The padding (=) explained
If your input doesn't divide evenly into 3 bytes, you pad with zero-bits and signal the original length with =:
- 3-byte input → 4 chars, no padding
- 2-byte input → 4 chars, 1
=at end - 1-byte input → 4 chars, 2
==at end
Some Base64 variants drop the padding entirely. JWT uses unpadded Base64URL.
The 33% size overhead
Every 3 bytes become 4 ASCII characters. For ASCII transport (JSON, email), that's a 33% size penalty. A 1MB image becomes ~1.33MB in Base64.
Plus: line wrapping in MIME Base64 adds ~1% more. PEM-encoded certificates wrap every 64 characters with newlines.
When Base64 is the right tool
- Embedding small binary in JSON — there's no native binary type in JSON
- Data URLs —
data:image/png;base64,...embeds an image inline in HTML/CSS - Email attachments (MIME) — SMTP requires 7-bit ASCII
- JWT payloads — header.payload.signature, all Base64URL
- HTTP Basic Auth —
username:passwordin Base64 - Crypto keys / certificates — PEM format wraps DER bytes in Base64
When Base64 is the wrong tool
- Large file uploads — use multipart/form-data or direct binary POST
- Database storage — store as BYTEA / BLOB, not Base64 text
- Network transport that supports binary — gRPC, WebSocket binary frames, raw TCP
- "Hiding" anything — Base64 is plain text once decoded
Base64URL — for URLs and JWTs
Standard Base64 uses + and /, which break URLs. Base64URL replaces them with - and _, and often drops padding.
| Standard | URL-safe |
|---|---|
| + | - |
| / | _ |
| = padding | (usually omitted) |
Encode/decode in different languages
// JavaScriptbtoa('hello')// "aGVsbG8="atob('aGVsbG8=')// "hello"// Node.jsBuffer.from('hello').toString('base64')Buffer.from('aGVsbG8=','base64').toString()# Pythonimportbase64 base64.b64encode(b'hello') base64.b64decode('aGVsbG8=')// Gobase64.StdEncoding.EncodeToString([]byte("hello"))
btoa() / atob() Unicode trap
JavaScript's btoa only works on Latin-1. Pass it a string with emoji or non-ASCII and it throws. The fix:
// Modern (Node 18+, modern browsers)constb64 =Buffer.from(text,'utf8').toString('base64');// Browser polyfillbtoa(unescape(encodeURIComponent(text)));
Tools
- Base64 encoder/decoder — quick browser-side encode/decode
- JWT decoder — uses Base64URL internally
- URL encoder — for percent-encoding (different from Base64)
Bottom line
Base64 is a binary-to-text encoding with 33% overhead. Use it for embedding small binary in text-only formats (JSON, URLs, email). Don't use it to "hide" data, don't use it for large files, and don't confuse Base64 with Base64URL — the latter is what JWTs and most modern URL-safe encodings use.
Try the free dev tools suite
15+ tools. 100% client-side. No signup. No tracking.
Browse all tools →