Base64 Encoding: What It Actually Is
Base64 takes binary data and turns it into plain ASCII text. It maps every 3 bytes of input into 4 characters from a 64-character alphabet (A-Z, a-z, 0-9, +, /), plus = for padding.
Hello becomes SGVsbG8=. The = at the end fills out the last chunk when your input isn't divisible by 3 bytes.
This is not encryption. Anyone can decode it instantly. No key, no secret. It exists for one reason: shoving binary data into places that only accept text.
What's actually happening
Base64 groups your input into 24-bit chunks (3 bytes), splits each chunk into four 6-bit values, then maps each value to one of 64 printable ASCII characters.
If your input isn't a multiple of 3 bytes, the encoder pads the output with = so it's always a multiple of 4 characters. One = means one byte of padding, == means two.
Same input, same output, every time. Fully deterministic and reversible.
How to use it
Pick Encode or Decode. Paste your input. Output updates automatically. Hit Copy.
When you'd actually reach for this
- Embedding a small image in CSS:
background: url(data:image/png;base64,iVBOR...) - Stuffing binary file data into a JSON API payload that only accepts strings
- Building an
Authorization: Basicheader — the spec requiresusername:passwordto be Base64-encoded - Encoding email attachments in MIME format, where binary has to survive as 7-bit ASCII
- Storing a small binary blob in an environment variable or config file
Base64 vs Base64URL
Standard Base64 uses +, /, and =. All three have special meaning in URLs. Base64URL swaps + for -, / for _, and usually drops the padding.
You'll run into Base64URL in JWTs (all three parts use it), query parameters (where + gets interpreted as a space), and filenames (where / is a path separator).
If you're decoding a JWT and your standard Base64 decoder is choking — that's almost always why. Swap - back to +, _ back to /, re-add padding, try again.
When NOT to use Base64
It's not security. I keep seeing people Base64-encode API keys thinking they're hidden. Anyone with a browser console can decode it in two seconds. Stop.
It adds 33% overhead. Every 3 bytes in becomes 4 bytes out. A 1 MB image becomes ~1.33 MB of text. For anything large, just serve the binary directly.
It kills compression. Base64 output has high entropy — gzip and brotli can barely touch it. A 100 KB image inlined as Base64 in your HTML will compress way worse than serving the image as a separate file.
Don't Base64-encode text to embed in other text. If you're encoding a JSON string to put inside another JSON string, you probably want to escape it. Different problem entirely.
Troubleshooting
Decoded output is garbled — check whether the original was encoded with btoa() (Latin-1 only) or Buffer.from() (UTF-8). In the browser, encode UTF-8 safely with btoa(unescape(encodeURIComponent(text))). In Node, use Buffer.from(text, 'utf8').toString('base64').
Output has unexpected characters — you're probably dealing with Base64URL, not standard Base64. Replace - with + and _ with /, pad to a multiple of 4 with =, then decode again.
btoa() throws on my string — it can't handle characters outside Latin-1. Emoji, CJK, anything with code points above 255 will fail. Use the encodeURIComponent workaround above or switch to Node's Buffer.
String has line breaks in it — that's valid. MIME Base64 wraps at 76 characters. Most decoders handle it fine, but strip whitespace first if yours doesn't.
Decoded file is corrupt — almost always trailing whitespace or a stray newline that got copied with the string. Trim and retry.
Data URIs feel slow — browsers support large data URIs but inlining a 500 KB image as Base64 in your CSS is a bad time. Keep data URIs under 5–10 KB. Anything bigger should be its own file request.
What to do with the output
If you encoded something — paste it into wherever needs text-safe binary: data URIs, API payloads, auth headers, config files.
If you decoded something — if it's a JWT payload, you can now read the claims directly without a library. If it's a file, save it. If it's text, it's just text again.