Every few months a security report describes credentials found "encrypted" in a config file, and the encryption turns out to be Base64. Decoding it takes one command and no key. The confusion is understandable and the consequences are not, so it is worth being precise about what Base64 actually does.
The Problem It Solves
Base64 exists because some channels carry text safely and binary data badly.
Email is the original example. SMTP was designed for 7-bit ASCII, and mail servers of the era would mangle anything else: stripping the high bit, converting line endings, interpreting byte sequences as control codes. Attaching a photograph meant finding a way to express arbitrary bytes using only characters that survive the trip.
Base64 does that by using 64 characters that are safe nearly everywhere: A-Z, a-z, 0-9, plus `+` and `/`. Any byte sequence can be written using only those, and any system that handles plain text will pass it through unchanged.
The same problem recurs constantly. Embedding an image directly in CSS, putting a binary value in a JSON string, passing a certificate through an environment variable: all are cases where the channel accepts text and the payload is bytes.
Three Bytes Become Four Characters
The mechanism is arithmetic rather than cryptography, and it takes a paragraph to describe fully.
Take three bytes, which is 24 bits. Split those 24 bits into four groups of six. Each 6-bit group has 64 possible values, and each value maps to one character in the alphabet. Three bytes in, four characters out.
That ratio is where the size increase comes from: output is 4/3 the size of input, roughly 33 percent larger, before any line breaks are added.
When the input length is not a multiple of three, the last group is short. Base64 pads it with zero bits, then appends `=` characters to record how much padding was added. One `=` means the final group came from two bytes; two `=` means it came from one. That is the whole explanation for the equals signs at the end of encoded strings, and it is why they appear on some strings and not others.
Variants You Will Encounter
Feeding a string to the wrong variant is a common source of decode failures.
| Variant | Characters 62 and 63 | Padding | Used by |
|---|---|---|---|
| Standard (RFC 4648 §4) | + and / | Yes | Email, data URLs, most APIs |
| URL-safe (RFC 4648 §5) | - and _ | Often omitted | JWTs, URL parameters, filenames |
| MIME | + and / | Yes | Email bodies; wraps at 76 characters |
| Base64url without padding | - and _ | No | JWT segments specifically |
Encoding is not encryption, and not obfuscation either
Base64 requires no key, so anyone who has the string has the data. A password, API token or private key stored as Base64 is stored in plaintext with an extra step. Treat encoded secrets exactly as you would treat unencoded ones: keep them out of source control, out of logs, and out of client-side code.
Where It Belongs and Where It Does Not
Base64 is the right answer for a narrow set of problems and a costly answer everywhere else.
Good: small images inlined in CSS
An icon under about 2KB inlined as a data URL saves an HTTP request. Above that the 33 percent size penalty and the loss of separate caching outweigh the saving.
Good: binary inside a text format
Certificates in PEM files, attachments in email, binary blobs in a JSON field. The channel is text and the payload is not, which is exactly the case Base64 was designed for.
Good: HTTP Basic auth headers
The credentials are Base64-encoded because the header must be ASCII. This provides no security at all, which is why Basic auth is only acceptable over HTTPS.
Bad: large files
Encoding a 5MB video adds 1.7MB and forces the whole thing through a text pipeline. Upload the bytes and reference them by URL.
Bad: anything you want to keep secret
Decoding is trivial and universally available. If confidentiality matters, encrypt; if integrity matters, sign. Base64 provides neither.
Encode or decode Base64
Full Unicode support, both directions, nothing sent to a server.
The Unicode Gotcha in the Browser
JavaScript's built-in `btoa()` throws an error on any character above U+00FF. Encoding a string containing an emoji, a Chinese character, or even a curly quote fails with "The string to be encoded contains characters outside of the Latin1 range".
The reason is historical: `btoa` expects a binary string where each character represents one byte, and JavaScript strings are UTF-16. Characters outside Latin-1 do not fit that assumption.
The correct fix is to convert the text to UTF-8 bytes first, using `TextEncoder`, then encode those bytes. Reverse the process on the way back with `TextDecoder`. The tool on this page does exactly that, which is why it handles any language and any emoji without complaint. If you have ever seen mojibake after a round trip through Base64, a missing UTF-8 conversion step is almost always the cause.
Frequently Asked Questions
Related Tools
Keep Reading
URL Encoding: encodeURI vs encodeURIComponent, and the Plus Sign
The two JavaScript functions are not interchangeable, and picking the wrong one is the most common URL bug there is. Plus where %2520 comes from.
JSON Errors Explained: Trailing Commas, NaN and Other Rejections
Why valid-looking JSON fails to parse, how to read a parser's error position, and the number precision bug that silently corrupts large IDs.
SHA-256 Explained: What Hashes Guarantee and What They Do Not
Three properties make a hash cryptographic, and each one supports a different use. Why SHA-256 is the wrong tool for storing passwords.
Password Strength: Why Length Beats Complexity Rules
NIST withdrew the advice about symbols and 90-day expiry, and the arithmetic explains why. One extra character is worth more than every symbol on the keyboard.