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() fails on "café" in JavaScript. Handle it properly:
const encoded = btoa(unescape(encodeURIComponent("café")));
// To decode:
const decoded = decodeURIComponent(escape(atob(encoded)));
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 Toolblip Base64 Encoder - drag in a PNG, JPEG, WebP, or SVG, and it generates the data URL instantly in your browser. Your image never touches a server.
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 } = 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(document), c => c.charCodeAt(0));
const docBlob = new Blob([docBytes], { type: "application/pdf" });
Decode in Python:
import base64
import json
response = requests.get("/api/user/123")
data = response.json()
avatar_bytes = base64.b64decode(data["avatar"])
with open("avatar.png", "wb") as f:
f.write(avatar_bytes)
JWT Payload Inspection
Every JWT has three Base64-encoded parts separated by dots. You can read the payload without any library:
JavaScript:
const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c3JfMTIzIiwibmFtZSI6IkFhcm9uIiwiaWF0IjoxNzQ...";
const [, payload] = token.split(".");
const decoded = JSON.parse(atob(payload));
console.log(decoded.sub); // usr_123
console.log(decoded.name); // Aaron
console.log(new Date(decoded.exp * 1000)); // Expiration date
Python:
import base64
import json
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c3JfMTIzIiwiY25hbWUiOiJhcm9vbiJ9.SflKxwR..."
# JWT uses "URL-safe" Base64 variant: replace - with + and _ with /
payload = token.split(".")[1]
payload += "=" * (4 - len(payload) % 4)
decoded = json.loads(base64.urlsafe_b64decode(payload))
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.
Dockerfile - encode a file into an ENV variable:
# Build args come in as strings; encode for safe transport
ARG SSL_CERT
ENV SSL_CERT_DATA=$(echo -n "$SSL_CERT" | base64)
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
This fires when atob() or btoa() hits a character outside the Latin1 range. Fix:
// WRONG - fails on non-ASCII
btoa("café"); // Error
// RIGHT
btoa(unescape(encodeURIComponent("café")));
atob(decodeURIComponent(escape(atob(encoded))));
Or use the native TextEncoder and TextDecoder:
const encoder = new TextEncoder();
const decoder = new TextDecoder();
const encoded = btoa(String.fromCharCode(...new Uint8Array(encoder.encode("café"))));
const decoded = decoder.decode(Uint8Array.from(atob(encoded), c => c.charCodeAt(0)));
Wrong Padding
Base64 strings should end with = or == for padding. If you're dealing with a server that strips padding:
import base64
# Add padding if missing
def pad(s):
return s + "=" * (4 - len(s) % 4)
decoded = base64.b64decode(pad(encoded))
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
Everything in this guide - text encoding, image conversion, data URL generation - works entirely in your browser with Toolblip Base64 Encoder/Decoder. No upload. No server call. No account.
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 for any input
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.