← Blog

How Base64 Encoding Works and When to Use It

January 12, 2026 · 7 min read

If you've done any web development, you've probably encountered Base64. Embedding images as strings, creating API auth headers... But why do we actually need it?

The Problem: Sending Binary Data as Text

All computer data is ultimately 0s and 1s. Images, audio files, compressed files—all binary data.

The problem? Systems like email and JSON only handle text. Put raw binary in there and things break.

The solution? Convert binary data into safe text characters. That's Base64.

How Base64 Works

The name "Base64" means it uses 64 characters.

A-Z (26) + a-z (26) + 0-9 (10) + '+' + '/' = 64

Plus = for padding.

The Conversion Process

  1. Convert original data to bits (0s and 1s)
  2. Group into 6-bit chunks (not 8-bit!)
  3. Map each 6-bit group to a Base64 character
  4. Process in 3-byte (24-bit) units. Pad with = if needed
ASCII 'M' = 77 'a' = 97 'n' = 110 3 × 8 = 24 bits Bits 010011 010110 000101 101110 Value 010011 = 19 010110 = 22 000101 = 5 101110 = 46 Char T W F u "Man" → "TWFu" 3 bytes (24 bits) → four 6-bit groups → 4 characters · ~33% larger
The core of Base64: three 8-bit bytes (24 bits) are re-split into four 6-bit values, each looked up in the 64-character table. Dashed lines mark the original byte boundaries.

Converting "Hi":

H = 01001000, i = 01101001

Concatenated: 01001000 01101001

6-bit groups: 010010 | 000110 | 1001(needs padding)

→ SGk=

Real-World Use Cases

1. Data URLs (Inline Images)

Embed small images directly in HTML/CSS without HTTP requests.

<img src="data:image/png;base64,iVBORw0KGgo..." />

Pros: Fewer HTTP requests. Cons: Larger HTML, no caching

2. HTTP Basic Authentication

The username:password is Base64 encoded and sent in the header.

Authorization: Basic dXNlcjpwYXNz

(user:pass Base64 encoded)

⚠️ Warning: Base64 is NOT encryption! Anyone can decode it. Always use HTTPS.

3. Email Attachments

Email uses a text protocol (SMTP). Attachments are Base64 encoded for transmission.

4. JWT Tokens

JWT header and payload are Base64Url encoded (similar to Base64 but URL-safe characters).

The Downsides

  • Size increase: ~33% larger than original (3 bytes → 4 characters)
  • Not encryption: Just obfuscation, anyone can decode
  • Processing overhead: Encoding/decoding uses CPU

When to Use (and When Not To)

✓ Good Use Cases ✗ Bad Use Cases
Small inline images Large file transfers (slow, inefficient)
Binary in JSON Hiding sensitive data (not encryption!)
Text protocol compatibility Big data (33% size increase)

Using Base64 in JavaScript

// Encode

btoa("Hello") // "SGVsbG8="

// Decode

atob("SGVsbG8=") // "Hello"

Note: btoa/atob only support ASCII. Unicode (like emojis) requires extra handling.

A Brief History of Base64

Base64 encoding did not appear out of thin air. It evolved over decades as email systems matured. The earliest formal specification was RFC 989 (1987), which defined Privacy Enhanced Mail (PEM) and included a base64 encoding for binary data within email messages. That spec was revised through RFC 1421, then RFC 2045 (MIME), and finally RFC 4648 (2006), which is the current authoritative reference.

The reason for this long evolution is that email was originally a 7-bit ASCII system. The Simple Mail Transfer Protocol (SMTP) could only reliably transmit characters in the 0-127 range. Binary attachments, characters from non-English alphabets, and even some control characters would get corrupted in transit. Base64 solved this by mapping arbitrary bytes to a safe 64-character alphabet that every mail relay could handle without modification.

RFC 4648 formalized not just Base64 but also Base32 and Base16 (hex), giving each a clear specification. It also defined the "URL-safe" Base64 alphabet, which replaced + with - and / with _ to avoid conflicts in URLs and filenames.

Base64 vs Other Encodings

Base64 is not the only way to represent binary data as text. Each alternative has trade-offs in size, readability, and compatibility.

Hexadecimal (Base16)

Hex encoding uses 16 characters (0-9, A-F). Each byte becomes two hex characters, meaning the encoded output is exactly twice the size of the input. That is a 100% size increase, compared to Base64's 33%. Hex is common in debugging, hash representations, and low-level programming because it maps directly to nibbles (4-bit groups), making it easy to read binary values.

"Hello" in hex:    48656c6c6f     (10 characters)
"Hello" in Base64: SGVsbG8=        (8 characters)
"Hello" raw:       5 bytes

URL Encoding (Percent Encoding)

URL encoding replaces unsafe characters with a percent sign followed by two hex digits. For example, a space becomes %20. Unlike Base64, URL encoding only transforms characters that are unsafe in a URL context. ASCII letters, digits, and a few symbols pass through unchanged. This makes it efficient for text that is mostly ASCII but contains a few special characters, and wildly inefficient for binary data (each byte becomes three characters in the worst case).

ASCII85 (Base85)

ASCII85 uses 85 printable ASCII characters to encode data, achieving roughly a 25% size increase (compared to Base64's 33%). It encodes four bytes into five characters. You will find it in Adobe PostScript and PDF files. It is more space-efficient than Base64 but less widely supported in web contexts. Unless you are working with PostScript or a system that specifically requires it, Base64 is the safer choice.

Base32

Base32 uses 32 characters (A-Z and 2-7). It produces output that is 60% larger than the input, less efficient than Base64. But it has a key advantage: the output is case-insensitive. This makes Base32 ideal for situations where case might be lost, such as DNS records, file systems on case-insensitive operating systems, or one-time passwords (TOTP/HOTP apps like Google Authenticator use Base32-encoded secrets).

Base58

Base58 was invented by Satoshi Nakamoto for Bitcoin addresses. It uses 58 characters, removing visually ambiguous ones: no zero (0), no uppercase O, no lowercase l, and no uppercase I. This reduces human transcription errors when typing or reading addresses. Base58 also avoids + and /, making the output URL-safe without modification. Ripple and IPFS also use Base58-based encodings.

Encoding Comparison Table

Encoding Characters Size Overhead Common Use
Hex (Base16) 16 +100% Hashes, debugging
Base32 32 +60% TOTP secrets, DNS
Base58 58 +37% Bitcoin, IPFS
Base64 64 +33% Email, Data URLs, JWT
ASCII85 85 +25% PDF, PostScript

Performance Considerations

Base64 encoding and decoding are fast operations. On modern hardware, a well-optimized implementation can process several gigabytes per second. The bottleneck is rarely the encoding itself but rather the 33% size increase and its downstream effects.

Consider a 1 MB image. Base64-encoded, it becomes roughly 1.37 MB of text. If you embed that in an HTML page, the browser must download the extra 370 KB, parse it as text, then decode it back to binary before rendering. A separate image file, on the other hand, can be cached independently, served from a CDN, and loaded in parallel with other resources. For images larger than a few kilobytes, separate files almost always win on performance.

In API contexts, sending large binary payloads as Base64 inside JSON is common but not always ideal. If the payload is large (several megabytes or more), consider multipart form uploads instead. The server avoids the decoding overhead and the network transfers fewer bytes.

Security Misconceptions

This cannot be stated enough: Base64 is not encryption, not obfuscation, not protection. It is a transport encoding. Every Base64 string can be decoded instantly by anyone with access to it. There is no key, no secret, no security whatsoever.

Yet developers regularly make mistakes here. Storing API keys as Base64 in source code does not hide them. Sending passwords as Base64 in HTTP headers (as in Basic Auth) does not protect them. Using Base64-encoded tokens without HTTPS transmits credentials in plaintext.

If you need to protect data in transit, use TLS (HTTPS). If you need to protect data at rest, use proper encryption (AES-256-GCM, for example). Base64 is a tool for encoding, not for securing.

Handling Unicode in Base64

The JavaScript functions btoa() and atob() only work with characters in the Latin-1 range (0-255). Passing a string with characters outside that range, such as emoji or CJK characters, throws an error. The standard workaround is to encode the string as UTF-8 first, then Base64-encode the resulting bytes.

// Encoding Unicode to Base64
function utoa(str) {
  return btoa(encodeURIComponent(str).replace(
    /%([0-9A-F]{2})/g,
    (_, p1) => String.fromCharCode('0x' + p1)
  ));
}

// Decoding Base64 to Unicode
function atou(b64) {
  return decodeURIComponent(
    atob(b64).split('').map(
      c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
    ).join('')
  );
}

utoa("Hello World!") // "SGVsbG8gV29ybGQh"
atou("SGVsbG8gV29ybGQh") // "Hello World!"

In Node.js, the Buffer class handles this more cleanly: Buffer.from(str, 'utf8').toString('base64'). Python's base64 module works with bytes objects, so you encode the string to UTF-8 first: base64.b64encode(text.encode('utf-8')).

Base64 in Modern Web Development

Despite being a decades-old standard, Base64 remains deeply embedded in modern web workflows. CSS background images can be inlined as data URIs for critical above-the-fold icons. Source maps in bundled JavaScript files often contain Base64-encoded original source code. Web fonts can be embedded inline to avoid FOIT (flash of invisible text). And practically every authentication system on the web uses Base64 somewhere in its token chain.

The key takeaway is knowing when Base64 adds value and when it adds waste. Small assets that block rendering benefit from inlining. Large assets, cacheable resources, and anything that changes independently from the HTML document should be kept as separate files. Understanding the encoding's mechanics and trade-offs lets you make that call with confidence.