Base64 turns arbitrary binary data into text using 64 characters that survive any transport: A–Z, a–z, 0–9, + and /, with = as padding.
Need to convert something right now? The base64 encoder and decoder handles UTF-8 and base64url in your browser.
How it works
Take three bytes - 24 bits. Split them into four groups of six bits. Each 6-bit group indexes into the 64-character alphabet.
Input: M a n
Bits: 01001101 01100001 01101110
Regroup: 010011 010110 000101 101110
Output: T W F u
Three bytes in, four characters out. That ratio is the whole story: base64 is always 33% larger than the input, plus padding.
When the input length is not a multiple of three, the last group is padded with = so the output length stays a multiple of four.
base64 vs base64url
The standard alphabet uses + and /, both of which have meaning in URLs - + can be read as a space, / as a path separator.
base64url swaps them: + becomes -, / becomes _, and the trailing = padding is usually dropped entirely.
This is why decoding a JWT with a plain base64 decoder sometimes fails. JWTs use base64url. The fix is two replacements and re-adding padding:
const decode = (segment) => {
const padded = segment.replace(/-/g, "+").replace(/_/g, "/");
return atob(padded + "=".repeat((4 - (padded.length % 4)) % 4));
};
The UTF-8 trap in JavaScript
btoa and atob operate on binary strings, one byte per character. Give btoa anything outside Latin-1 and it throws; atob on multi-byte content returns mojibake.
Correct round trip:
const encode = (text) =>
btoa(String.fromCharCode(...new TextEncoder().encode(text)));
const decode = (b64) =>
new TextDecoder().decode(
Uint8Array.from(atob(b64), (c) => c.charCodeAt(0)),
);
Skip the TextEncoder step and every accented character, emoji and non-Latin script breaks.
Base64 is not encryption
It provides zero confidentiality. Anyone can decode it in one line, and every browser has a decoder built in.
Things it does not do: hide an API key in your bundle, protect a config value, or make a token safe to log. If you are reaching for base64 to conceal something, you want encryption instead.
When inlining is worth it
Base64 in a data URI removes a network request at the cost of 33% more bytes and no caching.
Worth it: tiny assets under about 2KB - a small icon, a placeholder blur - where a round trip costs more than the extra bytes.
Not worth it: anything larger. A 100KB image becomes 133KB, cannot be cached separately, blocks the CSS or HTML it is embedded in from being parsed, and has to be re-downloaded whenever that file changes.
The number that decides it is round-trip latency versus transferred bytes. On HTTP/2 with many parallel requests, the round-trip cost collapsed and the case for inlining got much weaker than it was a decade ago.
If you are base64-encoding something to put in a JSON payload, check whether you can send it as a separate binary upload instead. You are paying a third more bandwidth for the convenience of one request.
Launch it where the numbers are checked
RankCert ranks products on domain control we verify ourselves. Listing is free.
