Developer tools

What Base64 is, when to use it and how to encode in JavaScript, Node and the terminal

Base64 turns binary data into text so it can travel safely. What it is, how much it grows the size, how to encode in JavaScript, Node.js and the terminal.

Base64 is a way of representing binary data (an image, a PDF, arbitrary bytes) using only 64 printable text characters: the letters A-Z and a-z, the digits 0-9 and the symbols + and /, with = as padding. It exists to carry binary data through channels designed for text, such as JSON, email, URLs or HTML attributes. It increases the size by roughly 33 % and it is not encryption: anyone can decode it.

This article explains how it works, when it makes sense (and when it does not) and how to encode and decode correctly in JavaScript, Node.js and the terminal, including the non-ASCII case that makes btoa fail.

How it works

Base64 takes the data 3 bytes (24 bits) at a time and splits them into 4 groups of 6 bits. Each 6-bit group (a value from 0 to 63) is replaced by a character from the table. If the length is not a multiple of 3, one or two = are appended to complete the last block.

That is where the cost comes from: every 3 bytes become 4 characters, so the result is 4/3 of the original (33 % more), rounded up to a multiple of 4. For example, the string Año 2026 ñ takes 12 bytes in UTF-8 and its Base64, QcOxbyAyMDI2IMOx, is 16 characters long.

The full specification is RFC 4648, which also defines the URL-safe variant: it replaces + with - and / with _ so the result can go in a URL or a file name without escaping. JWT tokens use this variant, usually without padding.

When to use Base64

  • Embedding a small resource in HTML, CSS or JSON: an SVG or PNG icon of a few KB as a data URL avoids an HTTP request.
  • Sending binary data in a JSON API when you cannot use multipart/form-data.
  • Email attachments: the MIME standard encodes attachments in Base64 because email is a text protocol.
  • HTTP basic authentication: the Authorization: Basic ... header carries user:password in Base64. That does not protect it, which is why it only makes sense over HTTPS.
  • Keys and certificates in PEM format, which are Base64 between -----BEGIN ... ----- lines.

When not to use it

  • To “hide” information. Base64 is decoded with one line of code. If you need confidentiality, encrypt.
  • For large images in HTML. A 300 KB photo in Base64 becomes 400 KB, is not cached separately and blocks HTML parsing. Above a few KB, serve the file from its own URL (and convert it to WebP).
  • For plain text in JSON. JSON already carries Unicode text; encoding it in Base64 only adds 33 % weight.

Encoding and decoding

In the browser: btoa and atob (carefully)

btoa() and atob() work with Latin-1 byte strings, not Unicode text. With characters outside that range (an ñ works; an emoji or Asian characters do not) btoa throws InvalidCharacterError. The correct approach is to go through TextEncoder first:

// Encode UTF-8 text to Base64
function toBase64(text) {
  const bytes = new TextEncoder().encode(text);
  let binary = '';
  for (const b of bytes) binary += String.fromCharCode(b);
  return btoa(binary);
}

// Decode Base64 to UTF-8 text
function fromBase64(b64) {
  const binary = atob(b64);
  const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0));
  return new TextDecoder().decode(bytes);
}

toBase64('Año 2026 ñ'); // "QcOxbyAyMDI2IMOx"
fromBase64('QcOxbyAyMDI2IMOx'); // "Año 2026 ñ"

For files (for example, an image picked by the user) use FileReader.readAsDataURL(), which returns a data URL with the content in Base64 directly.

In Node.js: Buffer

const b64 = Buffer.from('Año 2026 ñ', 'utf8').toString('base64'); // "QcOxbyAyMDI2IMOx"
const text = Buffer.from(b64, 'base64').toString('utf8'); // "Año 2026 ñ"

// URL-safe variant (no + / =)
const urlSafe = Buffer.from('Año 2026 ñ').toString('base64url');

// A whole file
import { readFile } from 'node:fs/promises';
const png = await readFile('icon.png');
const dataUrl = `data:image/png;base64,${png.toString('base64')}`;

Buffer handles character encoding correctly, so there is no need for the TextEncoder detour.

In the terminal

# Encode a file (GNU coreutils, Linux)
base64 image.png > image.b64

# Decode
base64 -d image.b64 > image.png

# Encode a string without the trailing newline
printf 'Año 2026 ñ' | base64

On macOS the decode option is -D in older versions and -d or --decode in recent ones. By default the GNU tool wraps the output at 76 characters; -w 0 prevents that.

Nothing to install

For a one-off conversion, the encoder on AIMRAN Tools encodes and decodes Base64 in the browser without sending the content to any server. It is the convenient option for inspecting a token, an attachment or a data URL.

Data URLs

A data URL embeds the content in the address itself: data:[MIME type][;base64],data.

<img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCI+PC9zdmc+" alt="" width="10" height="10" />

Useful for small icons and minimal CSS backgrounds. For SVG, embedding it as URL-escaped text is often more compact than Base64.

Common mistakes

  1. Using btoa with Unicode text without going through TextEncoder: InvalidCharacterError.
  2. Forgetting the MIME type in a data URL: the browser does not know what to do with the bytes.
  3. Mixing the standard and URL-safe variants: a + decoded as - corrupts the data. Use the same variant at both ends.
  4. Treating Base64 as security: credentials “encoded” in a public file are public credentials.
  5. Embedding large resources: more weight, no caching and slower HTML parsing.

Conclusion

Base64 solves one concrete problem: moving bytes through a text channel. Do it with Buffer in Node.js, with TextEncoder + btoa in the browser or with base64 in the terminal, use the URL-safe variant when the result goes in a URL, and remember its two limits: it grows the size by a third and it protects nothing.

Sources and references

  1. RFC 4648: The Base16, Base32, and Base64 Data Encodings rfc-editor.org
  2. MDN: Base64 developer.mozilla.org
  3. MDN: btoa() developer.mozilla.org
  4. Node.js: Buffer nodejs.org
  5. MDN: Data URLs developer.mozilla.org

Related tools

Herramientas gratuitas de AIMRAN Tools que funcionan en tu navegador, sin registro.

Ver todas las herramientas

Web development

How to convert images to WebP (and when not to)

What WebP is, how much it saves over JPEG and PNG, how to convert images in the browser, the terminal or Node.js, and how to serve them with picture.

4 min read