Base64 Encoding Explained: How It Works and When to Use It

Published March 2026 · 12 min read

What Is Base64 Encoding?

Base64 is a binary-to-text encoding scheme that represents binary data using a set of 64 printable ASCII characters. It was designed to solve a fundamental problem in computing: how do you transmit binary data — images, files, encrypted payloads — through systems that were built to handle only text?

Many protocols and data formats, such as email (SMTP), HTML, JSON, and XML, are inherently text-based. They cannot reliably transmit raw binary data because certain byte values may be interpreted as control characters, delimiters, or simply cause corruption. Base64 bridges this gap by converting arbitrary binary data into a safe, text-friendly representation that can pass through any text-based channel without modification.

The name "Base64" comes from the fact that the encoding uses exactly 64 characters to represent data. Compare this with Base16 (hexadecimal, using 16 characters) or Base256 (raw bytes). The choice of 64 is deliberate — it's a power of 2 (26 = 64), which makes the conversion from binary straightforward, while using enough characters to keep the encoded output reasonably compact.

Key takeaway: Base64 is not encryption, compression, or hashing. It is a reversible encoding that makes binary data safe for text-based transport. Anyone can decode it back to the original data.

How Base64 Encoding Works

The Base64 encoding process transforms every 3 bytes (24 bits) of input into 4 Base64 characters (each representing 6 bits). Here is the step-by-step algorithm:

The Base64 Alphabet

Base64 uses 64 characters drawn from the ASCII printable set. The alphabet consists of:

Step-by-Step Encoding Process

Let's walk through encoding the string "Hi!" to understand exactly how it works.

Step 1: Convert each character to its ASCII byte value

CharacterASCII DecimalBinary (8-bit)
H7201001000
i10501101001
!3300100001

Step 2: Concatenate all bits into one continuous binary string

01001000 01101001 00100001
→ 010010000110100100100001 (24 bits total)

Step 3: Split into 6-bit groups

010010 000110 100100 100001

Step 4: Convert each 6-bit group to its decimal value and map to a Base64 character

6-bit GroupDecimalBase64 Character
01001018S
0001106G
10010036k
10000133h

Result: "Hi!" encodes to "SGkh". Since the input was exactly 3 bytes, no padding is needed.

Understanding Padding

Base64 processes data in chunks of 3 bytes. When the input length is not a multiple of 3, padding is required. The = character is used as a pad:

For example, encoding "Hi" (only 2 bytes) produces "SGk=" — notice the single = pad. Encoding "H" (1 byte) produces "SA==" — two pad characters.

The Base64 Index Table

Here is the complete mapping from 6-bit values (0–63) to their corresponding Base64 characters. This table is the core of the encoding and decoding process.

IndexCharIndexCharIndexCharIndexChar
0A16Q32g48w
1B17R33h49x
2C18S34i50y
3D19T35j51z
4E20U36k520
5F21V37l531
6G22W38m542
7H23X39n553
8I24Y40o564
9J25Z41p575
10K26a42q586
11L27b43r597
12M28c44s608
13N29d45t619
14O30e46u62+
15P31f47v63/

The character selection is intentional. Uppercase letters come first (A=0), followed by lowercase (a=26), digits (0=52), and finally + and /. These 64 characters are universally safe in ASCII text systems, making them ideal for transport encoding.

Base64 in Web Development

Base64 encoding appears throughout modern web development. Here are the most common use cases you will encounter.

Data URIs — Embedding Images in CSS and HTML

Data URIs allow you to embed small files directly in HTML or CSS, eliminating an extra HTTP request. The format is:

data:[mediatype];base64,[encoded-data]

<!-- Example: Embedding a small PNG image -->
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUg..." alt="icon" />

/* In CSS */
.icon {
  background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxu...');
}

When to use Data URIs: Small icons (under 2–4 KB) where the overhead of an HTTP request exceeds the ~33% size increase from Base64. For larger images, external files with proper caching are more efficient.

API Authentication — HTTP Basic Auth

HTTP Basic Authentication encodes the username and password pair in Base64 and sends it in the Authorization header:

// Credentials: username = "admin", password = "secret123"
// Combined: "admin:secret123"
// Base64 encoded: "YWRtaW46c2VjcmV0MTIz"

Authorization: Basic YWRtaW46c2VjcmV0MTIz

Warning: Base64 is not encryption. The credentials above can be decoded by anyone who intercepts the header. Always use Basic Auth over HTTPS, and prefer more secure authentication methods like OAuth 2.0 or API keys when possible.

JSON Web Tokens (JWTs)

JWTs use a variant called Base64URL encoding for the header and payload sections. A JWT looks like header.payload.signature, where the header and payload are Base64URL-encoded JSON objects:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.   // header (Base64URL)
eyJzdWIiOiIxMjM0NTY3ODkwIn0.               // payload (Base64URL)
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c  // signature

Email Attachments (MIME)

Email was originally designed to transmit 7-bit ASCII text. MIME (Multipurpose Internet Mail Extensions) uses Base64 to encode binary attachments — images, PDFs, documents — so they can travel through email servers that only understand text. Every attachment you send or receive is Base64-encoded behind the scenes.

Storing Binary Data in JSON or XML

JSON and XML have no native binary type. When you need to include binary data — such as a cryptographic signature, a thumbnail, or a file hash — Base64 is the standard way to represent it as a string within these formats.

{
  "filename": "photo.jpg",
  "content": "R0lGODlhAQABAIAAAAAAAP///yH5BAEA...",
  "contentType": "image/jpeg"
}

Base64 in JavaScript

JavaScript provides several ways to work with Base64 encoding. Each approach has different capabilities and limitations.

btoa() and atob() — The Built-in Functions

The browser provides two global functions for Base64: btoa() (binary-to-ASCII, i.e., encode) and atob() (ASCII-to-binary, i.e., decode).

// Encoding
const encoded = btoa("Hello, World!");
// "SGVsbG8sIFdvcmxkIQ=="

// Decoding
const decoded = atob("SGVsbG8sIFdvcmxkIQ==");
// "Hello, World!"

Limitation: btoa() only handles Latin-1 characters (code points 0–255). Passing a string with characters outside this range — such as emoji or CJK characters — throws a DOMException.

Handling Unicode Strings

To encode strings containing Unicode characters beyond Latin-1, you first need to convert them to a byte sequence. Here is the modern approach using the TextEncoder API:

// Modern approach: TextEncoder + Uint8Array
function toBase64(str: string): string {
  const encoder = new TextEncoder();
  const bytes = encoder.encode(str);
  const binString = Array.from(bytes, (byte) =>
    String.fromCodePoint(byte)
  ).join("");
  return btoa(binString);
}

function fromBase64(base64: string): string {
  const binString = atob(base64);
  const bytes = Uint8Array.from(binString, (c) =>
    c.codePointAt(0)!
  );
  return new TextDecoder().decode(bytes);
}

// Works with any Unicode string
toBase64("Hello 🌍");   // "SGVsbG8g8J+MjQ=="
fromBase64("SGVsbG8g8J+MjQ=="); // "Hello 🌍"

Node.js Buffer API

In Node.js, the Buffer class provides a cleaner API for Base64 operations and handles Unicode natively:

// Encoding
const encoded = Buffer.from("Hello 🌍", "utf-8").toString("base64");
// "SGVsbG8g8J+MjQ=="

// Decoding
const decoded = Buffer.from("SGVsbG8g8J+MjQ==", "base64").toString("utf-8");
// "Hello 🌍"

// Encoding binary data (e.g., reading a file)
import { readFileSync } from "fs";
const fileBase64 = readFileSync("image.png").toString("base64");

Web APIs: FileReader and Fetch

When working with files in the browser, you can use the FileReader API or the Fetch API to convert files to Base64:

// Using FileReader
function fileToBase64(file: File): Promise<string> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => {
      const result = reader.result as string;
      resolve(result.split(",")[1]); // Remove data URI prefix
    };
    reader.onerror = reject;
    reader.readAsDataURL(file);
  });
}

// Using Fetch API (for URLs)
async function urlToBase64(url: string): Promise<string> {
  const response = await fetch(url);
  const blob = await response.blob();
  const buffer = await blob.arrayBuffer();
  const bytes = new Uint8Array(buffer);
  const binString = Array.from(bytes, (b) =>
    String.fromCodePoint(b)
  ).join("");
  return btoa(binString);
}

Base64URL vs Standard Base64

Standard Base64 uses + and / as its 62nd and 63rd characters, plus = for padding. This is problematic in URLs because all three characters have special meaning in the URL specification.

FeatureStandard Base64Base64URL
Character 62+-
Character 63/_
Padding=Omitted
URL safeNoYes

Base64URL (defined in RFC 4648) replaces + with - and / with _, and omits the padding = characters. This makes the output safe for use in URLs, filenames, and query parameters without any additional encoding.

// Converting between Standard Base64 and Base64URL
function toBase64Url(base64: string): string {
  return base64
    .replace(/\+/g, "-")
    .replace(/\//g, "_")
    .replace(/=+$/, "");
}

function fromBase64Url(base64url: string): string {
  let base64 = base64url
    .replace(/-/g, "+")
    .replace(/_/g, "/");
  // Restore padding
  while (base64.length % 4 !== 0) {
    base64 += "=";
  }
  return base64;
}

Base64URL is used extensively in JWTs, OAuth tokens, and anywhere Base64 data needs to appear in URLs or HTTP headers safely.

Common Misconceptions About Base64

Base64 is one of the most misunderstood encodings in software development. Here are the most common myths debunked.

Myth 1: "Base64 is Encryption"

Base64 is not encryption. Encryption requires a key and is designed to make data unreadable without that key. Base64 is a fully reversible, deterministic encoding with no key involved. Anyone can decode a Base64 string instantly using freely available tools. Never use Base64 to "protect" sensitive data like passwords or API keys.

Myth 2: "Base64 Makes Data Smaller"

The opposite is true. Base64 encoding always increases the data size by approximately 33%. Every 3 bytes of input become 4 bytes of output. If you need to reduce data size, use compression (gzip, brotli) instead. In fact, Base64-encoding already-compressed data partially negates the compression benefit.

Myth 3: "Base64 is Secure for Storing Passwords"

Base64 provides zero security. If you see a password or secret stored as Base64 in a configuration file, it is as exposed as if it were in plain text. For passwords, use proper hashing algorithms (bcrypt, scrypt, Argon2). For secrets in transit, use TLS/HTTPS. For secrets at rest, use proper encryption (AES-256-GCM or similar).

Remember: Base64 is an encoding, not a security mechanism. It transforms data for compatibility, not for confidentiality. If you need security, use encryption. If you need to verify integrity, use hashing. Base64 does neither.

Understanding the Size Overhead

Base64 encoding always increases the size of your data. Understanding why — and by how much — is important for making informed decisions about when to use it.

The Math Behind 33% Overhead

The overhead comes directly from the encoding ratio. Each group of 3 input bytes (24 bits) is represented as 4 Base64 characters (also 24 bits of information, but each character occupies a full byte in storage). That means:

With padding and line breaks (some implementations add line breaks every 76 characters as per MIME), the actual overhead can be slightly higher.

When the Overhead Matters

Original SizeBase64 SizeOverhead
1 KB1.33 KB+0.33 KB
10 KB13.3 KB+3.3 KB
100 KB133 KB+33 KB
1 MB1.33 MB+0.33 MB

Alternatives to Base64

Base64 is not always the best choice. Here are alternative approaches and when each one makes more sense.

Hexadecimal (Base16) Encoding

Hex encoding represents each byte as two hexadecimal characters (0-9, A-F). It has a 100% size overhead (doubles the data), making it less efficient than Base64. However, it is simpler to read and debug. Hex is commonly used for displaying hash values, color codes, and small binary values where human readability matters more than compactness.

// "Hi!" in different encodings
// Original bytes: [72, 105, 33]
// Hex:    "486921"        (6 chars — 100% overhead)
// Base64: "SGkh"          (4 chars — 33% overhead)
// Binary: "010010000110100100100001"  (24 chars)

Binary Protocols

When you control both the sender and receiver, binary protocols eliminate encoding overhead entirely:

Multipart Form Data

For file uploads over HTTP, use multipart/form-data instead of Base64-encoding the file in a JSON body. Multipart encoding transmits binary data directly without the 33% overhead:

// Instead of this (Base64 in JSON):
fetch("/api/upload", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    file: btoa(binaryData), // 33% larger!
    filename: "photo.jpg"
  })
});

// Prefer this (multipart form data):
const formData = new FormData();
formData.append("file", fileBlob, "photo.jpg");
fetch("/api/upload", {
  method: "POST",
  body: formData // Binary data sent directly
});

When to Choose Each Approach

ScenarioRecommended Approach
Small inline images (<4 KB)Base64 Data URI
File uploadsMultipart form data
Binary in JSON (small)Base64 encoding
Binary in JSON (large)Separate binary endpoint
High-performance APIsProtobuf / MessagePack
Debugging / loggingHex encoding
URLs / tokensBase64URL

Try It Yourself

Now that you understand how Base64 encoding works under the hood, put your knowledge into practice. Try encoding and decoding different strings, observe the padding behavior with various input lengths, and experiment with Unicode characters to see how the output changes.

Base64 Encoder & Decoder

Encode and decode Base64 strings instantly with our free online tool. Full UTF-8 support included.

Open Base64 Tool