What Base64 does
It turns every 3 bytes of binary into 4 printable characters, purely so data can travel through text-only protocols (email, JSON, URLs). It provides no confidentiality.
When to use it
- Carrying images or file content inside JSON (data URLs, attachments);
- Embedding certs or tokens in text config;
- When you need a human-reversible representation of binary.
Don't do this
- Use it as encryption: anyone can decode it, it is plaintext; use AES for secrets;
- Ignore the size: it grows ~33%, don't stuff huge files into JSON;
- Treat it as compression: Base64 grows data; use gzip to compress.
Try it
Encode and decode: Base64.
Real-world cases: three Base64 misuses
- Treating Base64 as encryption: an API returning
eyJpZCI6MX0=looks opaque, yet one decode restores it. Use AES for secrecy and signatures for tamper-evidence. - Inlining a large image slows first paint: a 500KB image as a CSS data URI grows about 33% and cannot be cached or fetched in parallel. Inline only tiny icons (a few KB); serve large images as files.
- Putting standard Base64 in a URL:
+,/and=get escaped or truncated and shift the parameters. In URLs use the URL-safe variant (+to-,/to_, drop=).
FAQ
Does Base64 compress anything? No — it grows the payload by about 33%. Why is the length a multiple of 4? Because every 3 bytes map to 4 characters, padded with =. Why does btoa fail on Chinese text? btoa accepts Latin-1 only; convert to bytes with TextEncoder first. Is it good for large database columns? No — it wastes space and is awkward to query; use a binary column or object storage.
Size and performance, measured
Once the cost of Base64 is quantified, the decision stops being a feeling:
- Size: encoded output grows about
33%(4/3), plus a few more points for padding and line breaks; - Memory: a Base64 string lives as UTF-16 in JS, so it can occupy more than twice the bytes of the original;
- Compression: gzip recovers much of the loss for large Base64 inside JSON (small alphabet, highly repetitive), but it cannot recover memory;
- Parsing: decoding a data URI means parsing a string then converting to bytes; multi-megabyte payloads block the main thread — use
Blobor a server endpoint instead.
Streaming large files
In Node, do not readFileSync a whole file and convert at once — encode in a stream, calling chunk.toString('base64') per chunk, and remember each chunk length must be a multiple of 3 or the boundaries produce broken padding. In browsers use FileReader.readAsDataURL or a Blob with URL.createObjectURL so the object never lands in memory as one string.
Three questions before using it
- Must this data cross a text-only channel? If not, do not encode it.
- Is it smaller than a few KB? Larger payloads belong in binary or a file.
- Is it long-term storage or a one-off transfer? Long-term storage belongs in object storage, with only a reference in the field.
Cross-platform compatibility notes
- Line breaks and whitespace: MIME-style Base64 wraps every 76 characters — strip whitespace and newlines before decoding or it fails;
- URL-safe variant: the
-and_form is not interchangeable with standard Base64; mix them and decoding breaks; - Optional padding: some implementations omit
=, but strict decoders reject it — keep padding when crossing systems; - Case sensitivity: Base64 is case-sensitive; any "just lowercase it" step corrupts the data.
A note on security boundaries
Base64 is often mistaken for light obfuscation. It offers no confidentiality: anyone who sees the output can decode it immediately. If you need to stop casual inspection, use keyed encryption or signatures, and keep "encoding" and "encryption" distinct in audits and compliance.
Order with compression
If you need both, compress first and then Base64-encode. Encoding first destroys the redundancy compression relies on, so the ratio collapses and the result can be larger than the original. On the way back, decode before decompressing.
Interaction with logging
Writing Base64 straight into logs inflates their size and hurts readability, and you cannot compare it with the original while debugging. If you must record it, also store a digest and length, or log it only at debug level to avoid permanently consuming storage and index capacity.