Base64 Encoding and Decoding: The Developer's Practical Cheatsheet
From text to images to JSON payloads - every Base64 operation a developer needs, explained with examples and linked directly to the free browser tool.
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):
const text = "Hello, World!";
const encoded = btoa(text); // "SGVsbG8sIFdvcmxkIQ=="
Python:
import base64
text = "Hello, World!"
encoded = base64.b64encode(text.encode("utf-8")).decode("utf-8")
# "SGVsbG8sIFdvcmxkIQ=="
Node.js:
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:
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:
const encoded = "SGVsbG8sIFdvcmxkIQ==";
const decoded = atob(encoded); // "Hello, World!"
Python:
import base64
encoded = "SGVsbG8sIFdvcmxkIQ=="
decoded = base64.b64decode(encoded).decode("utf-8")
# "Hello, World!"
Node.js:
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:
<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 for supported image files. The text Base64 tool 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):
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:
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):
# 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:
{
"id": "usr_123",
"name": "Avery Chen",
"avatar": "iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHL...",
"document": "JVBERi0xLjQKJeLjz9MKNSAwIG9iago8PC9MZW5ndGggNDA..."
}
Decode in 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:
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:
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:
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 - 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:
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:
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:
# .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:
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:
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.
// 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 for text and the 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 - inspect token payloads instantly
- JSON Formatter - validate and beautify JSON from any API response
- URL Encoder/Decoder - encode special characters for URLs and query strings
- Hash Generator - generate MD5, SHA-1, SHA-256, and more from text
Related reading:
- JWT Decoder Guide - deeper dive into JWT structure and claims
- JSON Debugging Guide - working with API responses in the browser
- Base64 Encoding Explained - the concept behind the code
Part of the Toolblip Developer Tools series - free, no-signup browser tools for developers.