# Base64 Encoding and Decoding: The Developer's Practical Cheatsheet

2026-04-28

https://toolblip.com/blog/2026-04-28-base64-encoding-decoding-complete-developer-guide

Every developer uses Base64 daily - in JWTs, data URLs, API responses, and config files. But when you need to *do* something with it, the documentation is scattered. This guide collects every Base64 operation you'll actually need, with working code and a browser tool to match.

No theory. No padding lecture. Just the stuff you Google at 11pm.

## Base64 in 60 Seconds

Base64 converts binary data into a string of 64 safe ASCII characters. It exists because JSON, HTML, HTTP headers, and email were all designed for text - not raw bytes.

The same string encoded in Base64 is about **37% larger** than the original binary. That's the price of safety.

**Rule of thumb:** Base64 is for *transport*, not *storage*. And it is absolutely not encryption.

## Text → Base64

The most common operation. You have a string, you need the Base64 version.

**JavaScript (browser or Node.js):**
```javascript
const text = "Hello, World!";
const encoded = btoa(text); // "SGVsbG8sIFdvcmxkIQ=="
```

**Python:**
```python
import base64
text = "Hello, World!"
encoded = base64.b64encode(text.encode("utf-8")).decode("utf-8")
# "SGVsbG8sIFdvcmxkIQ=="
```

**Node.js:**
```javascript
const Buffer = require("buffer").Buffer;
const text = "Hello, World!";
const encoded = Buffer.from(text).toString("base64");
```

**The pitfall most developers hit:** non-ASCII characters. `btoa("café")` succeeds because `é` fits in Latin-1, but it encodes Latin-1 bytes, not UTF-8. An emoji throws. Encode UTF-8 bytes first:

```javascript
const bytes = new TextEncoder().encode("café 🚀");
const encoded = btoa(String.fromCharCode(...bytes));
const decoded = new TextDecoder().decode(
  Uint8Array.from(atob(encoded), c => c.charCodeAt(0))
);
// decoded === "café 🚀"
```

## Base64 → Text

Reverse the operation to recover your original string.

**JavaScript:**
```javascript
const encoded = "SGVsbG8sIFdvcmxkIQ==";
const decoded = atob(encoded); // "Hello, World!"
```

**Python:**
```python
import base64
encoded = "SGVsbG8sIFdvcmxkIQ=="
decoded = base64.b64decode(encoded).decode("utf-8")
# "Hello, World!"
```

**Node.js:**
```javascript
const decoded = Buffer.from(encoded, "base64").toString("utf-8");
```

## Image → Base64 (Data URL)

This is where Base64 earns its keep. Converting an image to a Base64 data URL lets you embed it directly in HTML or CSS - no file to upload, no server to host it.

**When to use data URLs:**
- Small images: icons, logos, flags, UI glyphs
- Email HTML - attachments don't survive most email clients, but inline data URLs do
- Single-file demos and prototypes where you can't reference external assets
- Avoiding an extra HTTP request for a tiny image

**When NOT to use:**
- Large photos - a 500KB JPEG becomes a ~670KB Base64 string, and that's inside your HTML
- Anything that changes frequently - data URLs are static and uncacheable
- Production web pages - browsers cache regular image files, but inline data URLs are re-downloaded with every page load

**The format:**
```
data:[<mediatype>][;base64],<data>
```

Example:
```html
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA
AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot" />
```

To convert an image to Base64 without uploading it anywhere, use the **[Base64 Image Converter](/tools/images/base64-image-converter)** for supported image files. The [text Base64 tool](/tools/base64-encoder-decoder) has no file input.

## Base64 Image → File

Got a Base64 string and need the actual image file back? Here's how to decode it to disk.

**JavaScript (download in browser):**
```javascript
const base64Data = "iVBORw0KGgoAAAANSUhEUgAAAA...";
const mimeType = "image/png";

const blob = new Blob(
  [Uint8Array.from(atob(base64Data), c => c.charCodeAt(0))],
  { type: mimeType }
);

const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = "decoded-image.png";
link.click();
```

**Python:**
```python
import base64

with open("image.txt", "r") as f:
    base64_data = f.read().strip()

image_bytes = base64.b64decode(base64_data)

with open("image.png", "wb") as f:
    f.write(image_bytes)
```

**Bash (macOS/Linux with OpenSSL):**
```bash
# Decode and save
echo "iVBORw0KGgoAAAANSUhEUgAAAA..." | base64 -d > image.png

# macOS uses -D instead of -d
echo "iVBORw0KGgoAAAANSUhEUgAAAA..." | base64 -D > image.png
```

## Decoding API Responses

REST APIs love returning Base64-encoded data. Common patterns:

**JSON response with Base64 field:**
```json
{
  "id": "usr_123",
  "name": "Avery Chen",
  "avatar": "iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHL...",
  "document": "JVBERi0xLjQKJeLjz9MKNSAwIG9iago8PC9MZW5ndGggNDA..."
}
```

**Decode in JavaScript:**
```javascript
const response = await fetch("/api/user/123");
const { avatar, document: documentBase64 } = await response.json();

// Render avatar directly as data URL
const avatarUrl = `data:image/png;base64,${avatar}`;
document.querySelector("#avatar").src = avatarUrl;

// Decode document
const docBytes = Uint8Array.from(atob(documentBase64), c => c.charCodeAt(0));
const docBlob = new Blob([docBytes], { type: "application/pdf" });
```

**Decode in Python:**
```python
import base64
import requests

response = requests.get("https://api.example.com/user/123")  # replace with your API URL
data = response.json()

avatar_bytes = base64.b64decode(data["avatar"])
with open("avatar.png", "wb") as f:
    f.write(avatar_bytes)
```

## JWT Payload Inspection

A compact signed JWT (JWS) has three dot-separated base64url segments; a compact encrypted JWT (JWE) has five. This complete signed fixture is for reading the claims. Decoding does not verify its signature.

**JavaScript:**
```javascript
const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
const part = token.split(".")[1];
const base64 = part.replace(/-/g, "+").replace(/_/g, "/");
const bytes = Uint8Array.from(atob(base64.padEnd(Math.ceil(base64.length / 4) * 4, "=")), c => c.charCodeAt(0));
const decoded = JSON.parse(new TextDecoder().decode(bytes));
console.log(decoded.name); // John Doe
```

**Python:**
```python
import base64
import json

token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
payload = token.split(".")[1]
payload += "=" * ((-len(payload)) % 4)
decoded = json.loads(base64.urlsafe_b64decode(payload))
print(decoded["name"])  # John Doe
```

> **Note:** You can decode a JWT payload instantly in the browser with the **[Toolblip JWT Decoder](/tools/jwt-decoder)** - no library, no install, entirely client-side.

## Base64 in Configuration Files

Many config formats (Docker, Docker Compose, `.env` files, SSH keys) use Base64 to store binary data as text.

**Host-side Base64 for a nonsensitive constant:**
```bash
printf '%s' 'example-value' | base64
```

Dockerfile `ENV` does not run shell commands. Base64 does not protect secrets, and values baked into image layers remain accessible. Use runtime secret injection for credentials.

**docker-compose.yml - storing credentials:**
```yaml
secrets:
  api_key:
    file: ./api_key.txt   # The raw key file on disk

# Or inline (for non-sensitive dev values):
environment:
  - CERT_DATA=dGVzdC1rZXktaGVyZQ==
```

**`.env` files and binary values:**
```bash
# .env
SERVICE_ACCOUNT_CREDENTIALS={"type":"service_account",...}
# This JSON string gets Base64-encoded when passed through certain CI systems.
# Decode with: echo "$SERVICE_ACCOUNT_CREDENTIALS" | base64 -d
```

## Common Errors and How to Fix Them

### `InvalidCharacterError` in JavaScript

`btoa()` accepts code units through U+00FF, but it does not encode text as UTF-8. `btoa("café")` returns `Y2Fm6Q==`, while `btoa("🚀")` throws. For UTF-8 text, use `TextEncoder` and `TextDecoder`:

```javascript
const bytes = new TextEncoder().encode("café 🚀");
const encoded = btoa(String.fromCharCode(...bytes));
const decoded = new TextDecoder().decode(
  Uint8Array.from(atob(encoded), c => c.charCodeAt(0))
);
// decoded === "café 🚀"
```

### Wrong Padding

Base64 uses zero, one, or two padding characters as needed. Some decoders accept unpadded input; when a strict decoder needs padding, add only enough to reach a multiple of four:

```python
import base64

# Add padding if missing
encoded = "SGk"  # unpadded Base64 for Hi
def pad(s):
    return s + "=" * ((-len(s)) % 4)

decoded = base64.b64decode(pad(encoded))
print(decoded.decode("utf-8"))  # Hi
```

### URL-Safe Base64 vs. Standard Base64

Standard Base64 uses `+` and `/`. URL-safe Base64 replaces them with `-` and `_` and strips padding. This variant is used in JWTs, OAuth tokens, and some API keys.

```javascript
// Standard Base64
const standard = btoa("hello?world"); // "aGVsbG8/d29ybGQ="

// URL-safe Base64
const urlSafe = standard.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
// "aGVsbG8_d29ybGQ"
```

## The Quick Reference

| Operation | JavaScript | Python | Node.js |
|-----------|-----------|--------|---------|
| Encode text | `btoa(str)` | `base64.b64encode(str)` | `Buffer.from(str).toString("base64")` |
| Decode text | `atob(str)` | `base64.b64decode(str)` | `Buffer.from(str, "base64").toString()` |
| Encode bytes | `btoa(String.fromCharCode(...bytes))` | `base64.b64encode(bytes)` | `Buffer.from(bytes).toString("base64")` |
| Decode to bytes | `Uint8Array.from(atob(str), c => c.charCodeAt(0))` | `base64.b64decode(str)` | `Buffer.from(str, "base64")` |

## Try It in Your Browser

Use the **[Base64 Encoder/Decoder](/tools/base64-encoder-decoder)** for text and the **[Base64 Image Converter](/tools/images/base64-image-converter)** for supported image files. Both perform the conversion in your browser.

Open a tab, paste, and you're done.

---

**Related tools on Toolblip:**
- [JWT Decoder](/tools/jwt-decoder) - inspect token payloads instantly
- [JSON Formatter](/tools/json-formatter) - validate and beautify JSON from any API response
- [URL Encoder/Decoder](/tools/url-encode) - encode special characters for URLs and query strings
- [Hash Generator](/tools/md5-hash-generator) - generate MD5, SHA-1, SHA-256, and more from text

**Related reading:**
- [JWT Decoder Guide](/blog/jwt-decoder-guide) - deeper dive into JWT structure and claims
- [JSON Debugging Guide](/blog/2026-04-26-json-debugging-guide-browser-tools) - working with API responses in the browser
- [Base64 Encoding Explained](/blog/base64-encoding-explained) - the concept behind the code

---
*Part of the [Toolblip Developer Tools](/tools) series - free, no-signup browser tools for developers.*
