Base64 turns arbitrary binary data into a string of 64 safe ASCII characters: A-Z, a-z, 0-9, + and /, with = used as padding. That is the entire idea. It is not compression, it is not encryption, and it is not obscure — but it is misunderstood often enough to cause real bugs.
Why it exists
Plenty of systems were designed to carry text and behave badly when handed raw bytes. Email is the canonical example: SMTP historically assumed 7-bit ASCII, and a byte with the high bit set could be mangled in transit. Older protocols also treat certain byte values as control characters, silently corrupting anything that contains them.
Base64 solves this by re-expressing binary in a character set that survives any text channel. Three bytes (24 bits) become four characters of six bits each. When the input length is not divisible by three, = pads the output.
The cost is size: Base64 output is about 33% larger than the input. A 3 MB image becomes roughly 4 MB of text. That overhead is the reason most of the "should I use Base64 here?" answers come out as "no".
Where it is genuinely the right tool
Email attachments. MIME still encodes binary attachments as Base64. This is Base64's original job and it does it well.
Data URIs for tiny assets. data:image/svg+xml;base64,... embeds an image directly in HTML or CSS, saving an HTTP request. Worth it for icons under about 2-3 KB. Above that the 33% bloat, the loss of caching, and the render-blocking cost of a fatter stylesheet outweigh the saved request — especially over HTTP/2 and HTTP/3, where extra requests are cheap.
Binary inside JSON. JSON has no binary type. If you must ship a file inside a JSON payload — a signature, a small thumbnail, a certificate — Base64 is the standard answer. For anything large, upload separately and pass a URL.
Cryptographic material. Keys, certificates, hashes and signatures are routinely exchanged as Base64. A PEM file is Base64 with header and footer lines.
HTTP Basic auth headers. Authorization: Basic dXNlcjpwYXNz is user:pass Base64-encoded. Note what that means: it is trivially reversible, which is exactly why Basic auth is unacceptable without TLS.
JWT tokens. The header and payload of a JWT are Base64URL-encoded JSON, which is why you can read a token's claims without any secret. Paste one into the JWT Decoder and see for yourself — and note that the signature is what provides security, not the encoding.
Base64URL: the variant that prevents bugs
Standard Base64 uses + and /, both of which are meaningful in URLs — + decodes to a space in query strings and / is a path separator. Padding = also causes trouble in some parsers.
Base64URL substitutes - for + and _ for /, and usually drops the padding. Use it for anything that travels in a URL, a filename, or a JWT. Mixing the two variants is a classic source of "works in my test, fails in production" failures.
If you need to pass standard Base64 in a URL, percent-encode it with the URL Encoder instead of hoping.
The Unicode trap
In JavaScript, btoa() and atob() operate on Latin-1 code points and throw on anything outside that range:
btoa("héllo") // InvalidCharacterError
The classic fix encodes to UTF-8 bytes first:
const encode = (s) => btoa(unescape(encodeURIComponent(s)));
const decode = (s) => decodeURIComponent(escape(atob(s)));
In modern environments, TextEncoder and TextDecoder are the cleaner route:
const bytes = new TextEncoder().encode("héllo");
const b64 = btoa(String.fromCharCode(...bytes));
Node.js sidesteps the whole issue:
Buffer.from("héllo", "utf8").toString("base64");
Buffer.from(b64, "base64").toString("utf8");
The Base64 Encoder handles UTF-8 correctly in both directions, so emoji, accented characters and non-Latin scripts round-trip cleanly.
It is not security
This deserves its own heading because the mistake is common and expensive.
Base64 is a public, reversible transformation with no key. Anyone can decode it instantly. Encoding a password, an API key or a session token in Base64 provides exactly zero protection — it only makes the value slightly less obvious to a casual reader, which is often worse, because it creates a false sense of safety.
Never Base64-encode a secret and call it protected. Encrypt it with a real algorithm, transmit over TLS, and store it in a secrets manager. If you need a one-way transformation to verify data integrity, that is hashing — use the Hash Generator for SHA-256.
Debugging Base64 problems
Invalid character errors usually mean whitespace, newlines or URL-encoded characters snuck in. PEM files, for instance, wrap at 64 characters — strip the line breaks before decoding.
Wrong padding. Length must be a multiple of four after padding. Some encoders drop =; append it back if your decoder is strict.
Variant mismatch. Seeing - and _ means Base64URL. Convert before feeding it to a standard decoder.
Double encoding. If decoding produces another Base64-looking string, it was encoded twice somewhere in the pipeline.
Corrupted binary output. Almost always a charset problem — you decoded bytes as text somewhere along the way.
Rules of thumb
- Encode when a binary payload must cross a text-only channel.
- Do not encode large files into JSON or HTML; use a URL.
- Use Base64URL in URLs, filenames and tokens.
- Handle UTF-8 explicitly in JavaScript.
- Never treat it as a security measure.
- Remember the 33% size penalty when budgeting payloads.
Try it with the Base64 Encoder — encode and decode locally in your browser, with nothing sent to a server. Related developer utilities live in the Developer tools category.