Base64 to Image: Decode in JS, Python, Node, PHP, Bash
Base64-encoded images appear everywhere in software systems. API responses return them as JSON string fields. Data URIs embed them directly in HTML and CSS. Email attachments encode them for MIME transport. Databases store binary image data as base64 text columns because raw bytes break most serialization formats.
At some point, you need the actual image file back. You need to decode that base64 string into the original binary image data and either save it to disk, display it in a browser, or pipe it into a processing tool.
This article gives you working decode code in five languages, explains the data URI format you will encounter constantly, and covers the performance cases where base64 is the wrong choice entirely. If you need to convert or optimize the resulting image after decoding, Pixotter's format conversion tools handle that in your browser with no upload required.
Jump to: JavaScript | Python | Node.js | Bash | PHP | Data URI format | When NOT to use base64
What Is Base64 Image Encoding?
Base64 is a binary-to-text encoding scheme defined in RFC 4648. It takes arbitrary binary data — like the bytes of a PNG file — and represents them using 64 printable ASCII characters: A-Z, a-z, 0-9, +, and /, with = for padding.
The encoding works by taking three bytes of input (24 bits) and splitting them into four 6-bit groups. Each 6-bit group maps to one of the 64 characters. This means every 3 bytes of binary data become 4 bytes of base64 text — a 33% size increase.
A 100 KB image becomes approximately 133 KB when base64-encoded. A 1 MB image becomes 1.33 MB. This overhead is the fundamental trade-off of base64: you gain text-safe transportability, you lose size efficiency.
Why Base64 Encoding Exists
Binary data breaks things. JSON cannot contain raw bytes. HTML source is text. Email protocols were designed for 7-bit ASCII. XML parsers choke on null bytes. Base64 solves all of these by converting binary into safe ASCII text that passes through any text-based transport.
Common scenarios where you encounter base64-encoded images:
- REST API responses — APIs that return image data inline (rather than as a URL) encode images as base64 strings. Common with AI image generation APIs, document processing services, and screenshot APIs.
- Data URIs in HTML/CSS — The
data:image/png;base64,...format embeds images directly in markup, eliminating an HTTP request. Used for small icons, 1x1 tracking pixels, and CSS background images. - Email attachments — MIME encoding uses base64 to embed binary attachments within text-based email messages.
- Database storage — Storing images in text columns (PostgreSQL
text, MySQLLONGTEXT, MongoDB string fields) requires base64 encoding since these columns do not support raw binary. - Configuration files — Kubernetes Secrets,
.envfiles, and JWT payloads encode binary data as base64.
Try it yourself
Convert between any image format instantly — free, instant, no signup. Your images never leave your browser.
How to Decode Base64 to Image
Every major language has base64 decode built into its standard library. The core operation is the same everywhere: take the base64 string, strip any data URI prefix if present, decode to bytes, and write those bytes to a file or create a blob.
JavaScript (Browser)
Two approaches in the browser, depending on whether you need a file download or an in-page display.
Method 1: Using atob() and Blob
// Base64 string (without the data URI prefix)
const base64String = "iVBORw0KGgoAAAANSUhEUgAA...";
// Decode base64 to binary string
const binaryString = atob(base64String);
// Convert binary string to Uint8Array
const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
// Create a Blob and generate an object URL
const blob = new Blob([bytes], { type: "image/png" });
const imageUrl = URL.createObjectURL(blob);
// Display in an <img> element
const img = document.createElement("img");
img.src = imageUrl;
document.body.appendChild(img);
// Clean up when done (prevents memory leaks)
img.onload = () => URL.revokeObjectURL(imageUrl);
Method 2: Using fetch() with a data URI
This is cleaner and avoids the manual byte conversion loop:
const base64String = "iVBORw0KGgoAAAANSUhEUgAA...";
const dataUri = `data:image/png;base64,${base64String}`;
const response = await fetch(dataUri);
const blob = await response.blob();
// Use the blob: display it, download it, or send it
const imageUrl = URL.createObjectURL(blob);
document.getElementById("preview").src = imageUrl;
The fetch() approach handles the decode, MIME type parsing, and blob creation in two lines. It works in all modern browsers (Chrome 42+, Firefox 39+, Safari 10.1+, Edge 14+).
Triggering a download:
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = "decoded-image.png";
link.click();
Python 3.12
import base64
from pathlib import Path
# Base64 string — may come from an API response, database, etc.
base64_string = "iVBORw0KGgoAAAANSUhEUgAA..."
# Strip the data URI prefix if present
if base64_string.startswith("data:"):
base64_string = base64_string.split(",", 1)[1]
# Decode and write to file
image_bytes = base64.b64decode(base64_string)
Path("output.png").write_bytes(image_bytes)
print(f"Saved {len(image_bytes)} bytes to output.png")
For processing the decoded image further (resize, compress, convert format), use Pillow 10.4:
import base64
import io
from PIL import Image # Pillow 10.4
base64_string = "iVBORw0KGgoAAAANSUhEUgAA..."
image_bytes = base64.b64decode(base64_string)
image = Image.open(io.BytesIO(image_bytes))
print(f"Format: {image.format}, Size: {image.size}")
# Convert to JPEG and save
image.convert("RGB").save("output.jpg", "JPEG", quality=85)
Node.js 22
import { writeFileSync } from "node:fs";
import { Buffer } from "node:buffer";
const base64String = "iVBORw0KGgoAAAANSUhEUgAA...";
// Strip data URI prefix if present
const cleanBase64 = base64String.includes(",")
? base64String.split(",")[1]
: base64String;
// Decode and write
const imageBuffer = Buffer.from(cleanBase64, "base64");
writeFileSync("output.png", imageBuffer);
console.log(`Saved ${imageBuffer.length} bytes to output.png`);
For processing multiple base64 images from an API response:
import { writeFileSync } from "node:fs";
import { Buffer } from "node:buffer";
// Typical API response with multiple base64 images
const apiResponse = {
images: [
{ name: "thumbnail.png", data: "iVBORw0KGgo..." },
{ name: "full.jpg", data: "/9j/4AAQSkZJ..." },
],
};
for (const { name, data } of apiResponse.images) {
const buffer = Buffer.from(data, "base64");
writeFileSync(name, buffer);
console.log(`Decoded ${name} (${buffer.length} bytes)`);
}
Bash (GNU Coreutils)
# Decode a base64 string from a file
base64 -d input.b64 > output.png
# Decode an inline string
echo "iVBORw0KGgoAAAANSUhEUgAA..." | base64 -d > output.png
# Decode from a data URI (strip the prefix first)
cat data-uri.txt | sed 's/^data:image\/[^;]*;base64,//' | base64 -d > output.png
# On macOS, use -D instead of -d
base64 -D input.b64 > output.png
Note the macOS difference: GNU coreutils uses base64 -d (lowercase), while macOS BSD uses base64 -D (uppercase). If your script needs to run on both, detect the platform:
if [[ "$(uname)" == "Darwin" ]]; then
BASE64_DECODE="base64 -D"
else
BASE64_DECODE="base64 -d"
fi
echo "$BASE64_STRING" | $BASE64_DECODE > output.png
PHP 8.3
<?php
$base64String = "iVBORw0KGgoAAAANSUhEUgAA...";
// Strip data URI prefix if present
if (str_starts_with($base64String, 'data:')) {
$base64String = explode(',', $base64String, 2)[1];
}
// Decode
$imageData = base64_decode($base64String, strict: true);
if ($imageData === false) {
throw new RuntimeException('Invalid base64 input');
}
// Write to file
file_put_contents('output.png', $imageData);
echo sprintf("Saved %d bytes to output.png\n", strlen($imageData));
The strict: true parameter (second argument to base64_decode) rejects input containing characters outside the base64 alphabet. Always use it — without strict mode, PHP silently ignores invalid characters, which can produce corrupted output from malformed input.
Online Tools
If you have a base64 string and need to quickly convert it to an image file, paste it into any browser console using the JavaScript methods above. For further processing — converting the decoded PNG to WebP, compressing a decoded JPEG, or batch-converting multiple formats — Pixotter's conversion tools handle it entirely client-side.
The advantage of client-side tooling: your decoded images never leave your machine. Base64-encoded images often come from API keys, user data, or internal systems. Uploading them to a third-party online converter defeats the purpose of handling them securely.
Base64 Data URI Format
Data URIs are the most common way base64 images appear in web contexts. The format is defined in RFC 2397:
data:[<mediatype>][;base64],<data>
A complete example:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==
Breaking this apart:
| Component | Value | Purpose |
|---|---|---|
| Scheme | data: |
Identifies this as a data URI |
| MIME type | image/png |
Tells the browser how to interpret the bytes |
| Encoding | ;base64 |
Indicates base64 encoding (as opposed to URL-encoded text) |
| Separator | , |
Divides the metadata from the data |
| Data | iVBORw0KGgo... |
The actual base64-encoded image bytes |
MIME Types for Common Image Formats
| Format | MIME Type | Data URI Prefix |
|---|---|---|
| PNG | image/png |
data:image/png;base64, |
| JPEG | image/jpeg |
data:image/jpeg;base64, |
| GIF | image/gif |
data:image/gif;base64, |
| WebP | image/webp |
data:image/webp;base64, |
| AVIF | image/avif |
data:image/avif;base64, |
| SVG | image/svg+xml |
data:image/svg+xml;base64, |
| ICO | image/x-icon |
data:image/x-icon;base64, |
| BMP | image/bmp |
data:image/bmp;base64, |
Detecting Image Format from Base64
You do not always receive the MIME type. If you only have the raw base64 string, decode the first few bytes and check the magic bytes:
| Format | First bytes (hex) | Base64 starts with |
|---|---|---|
| PNG | 89 50 4E 47 |
iVBORw0KGgo |
| JPEG | FF D8 FF |
/9j/ |
| GIF | 47 49 46 38 |
R0lGOD |
| WebP | 52 49 46 46 |
UklGR |
| BMP | 42 4D |
Qk |
This is useful when an API returns a base64 string without specifying the format — check the first characters to determine the file extension.
When NOT to Use Base64
Base64 embedding has legitimate uses, but it is frequently overused. The 33% size increase is only the start of the problem.
The Performance Case Against Base64
Increased transfer size. A 50 KB image becomes 67 KB as base64 text. Multiply across every image on a page, and the overhead compounds. If you have ten 50 KB images embedded as base64, you are transferring 170 KB of unnecessary data compared to external files.
No browser caching. An external image file is cached after the first request. A base64-encoded image embedded in HTML or CSS is re-downloaded with every page load. For images that appear on multiple pages (logos, icons, UI elements), this eliminates one of the browser's most effective performance optimizations.
Blocks rendering. Base64 images embedded in CSS files block rendering until the entire stylesheet is parsed. An external image loaded via <img> can be deferred, lazy-loaded, or loaded asynchronously. A base64 image in a background-image property cannot.
No responsive loading. External images work with srcset, <picture>, and responsive breakpoints. A base64 image is a single fixed resolution. You cannot serve different sizes to different devices without duplicating the entire encoded string.
Breaks Content Security Policy. If your CSP restricts inline data (data: scheme), base64 data URIs will be blocked. External image references are easier to whitelist.
Base64 Embedding vs External File
| Factor | Base64 Embedded | External File |
|---|---|---|
| HTTP requests | 0 (inline) | 1 per image |
| Browser caching | No | Yes |
| File size overhead | +33% | None |
| Render blocking | Yes (in CSS) | No (async/lazy) |
| Responsive images | Not supported | srcset, <picture> |
| CDN delivery | Not applicable | Yes |
| CSP compatibility | Requires data: source |
Standard img-src |
| Best use case | Icons < 2 KB, single-use SVGs | Photos, illustrations, anything > 2 KB |
When Base64 Actually Makes Sense
- Images under 2 KB. A 1 KB SVG icon as a data URI adds approximately 1.3 KB to the HTML but eliminates an HTTP request. At this size, the request overhead (DNS lookup, TCP handshake, TLS negotiation) often exceeds the base64 size penalty.
- Email HTML. Email clients do not reliably load external images. Embedding small images as base64 guarantees they display without user intervention.
- Single-page applications with build pipelines. Tools like Vite (6.2) and webpack (5.98) can inline small assets as base64 automatically during bundling, with configurable size thresholds.
- Data transport. Moving an image through a JSON API, a message queue, or a database text field where binary is not supported.
For anything else — product images, blog photos, hero banners, background images — use external files served from a CDN. Compress them with a tool like Pixotter's image compressor before serving, and you will get better performance than any base64 approach.
Handling Common Edge Cases
Line Breaks in Base64 Strings
Some encoders insert line breaks every 76 characters (per RFC 2045 MIME specification). Most decoders handle this, but if yours does not, strip them first:
// JavaScript
const cleaned = base64String.replace(/[\r\n]/g, "");
# Python 3.12
cleaned = base64_string.replace("\n", "").replace("\r", "")
URL-Safe Base64
Some APIs use URL-safe base64, which replaces + with - and / with _. Standard decoders will fail on this input. Convert before decoding:
// JavaScript — convert URL-safe base64 to standard base64
const standard = urlSafeBase64.replace(/-/g, "+").replace(/_/g, "/");
# Python 3.12 — built-in support for URL-safe base64
import base64
image_bytes = base64.urlsafe_b64decode(url_safe_base64_string)
Padding Issues
Base64 strings must have a length divisible by 4. Missing = padding characters cause decode failures. If you receive unpadded base64:
// JavaScript — add padding
const padded = base64String + "=".repeat((4 - (base64String.length % 4)) % 4);
Frequently Asked Questions
How do I convert a base64 string to an image file?
Decode the base64 string to binary data using your language's standard library (atob() in JavaScript, base64.b64decode() in Python, Buffer.from() in Node.js, base64_decode() in PHP, base64 -d in Bash), then write the binary data to a file with the correct extension.
What image formats can be base64-encoded?
Every image format works with base64 encoding — PNG, JPEG, GIF, WebP, AVIF, SVG, BMP, ICO, and TIFF. Base64 encodes raw bytes, so the image format is irrelevant to the encoding process. The format only matters when you write the decoded bytes to a file and need the correct extension.
How do I determine the image format from a base64 string?
Check the first few characters of the base64 string. PNG starts with iVBORw0KGgo, JPEG starts with /9j/, GIF starts with R0lGOD, and WebP starts with UklGR. These correspond to each format's magic bytes. If the string is a data URI, the MIME type is specified in the prefix (data:image/png;base64,).
Why is my base64-decoded image corrupted?
Three common causes: (1) the base64 string was truncated during copy or transport — verify the full string was captured, (2) the string uses URL-safe base64 (- and _ instead of + and /) and your decoder expects standard base64, (3) the string has missing padding (= characters). Check the string length is divisible by 4 and contains only valid base64 characters.
Is base64 encoding secure for images?
Base64 is an encoding, not encryption. It provides zero security. Anyone can decode a base64 string instantly. Do not use base64 as a way to hide or protect image data. If you need to protect images in transit, use HTTPS. If you need to protect images at rest, use actual encryption (AES-256-GCM or similar).
Should I store images as base64 in my database?
Generally, no. Base64-encoded images in database text columns are 33% larger than the original binary, cannot be streamed, and increase query payload size. Store images as binary blobs (BYTEA in PostgreSQL, LONGBLOB in MySQL) or, better, store them in object storage (S3, GCS, R2) and keep only the URL in the database. Base64 database storage is acceptable only for small images (avatars under 5 KB, icons) where the operational simplicity of a single query outweighs the size overhead.
What to Do After Decoding
Once you have the decoded image file, you may need to optimize it before serving it on the web. An image from an API response or database is rarely web-optimized.
- Wrong format? Convert the decoded image to a modern web format like WebP or AVIF using Pixotter's format converter. Client-side conversion means the file never leaves your machine.
- Too large? Compress the image to reduce file size without visible quality loss.
- Need the reverse operation? See our guide on encoding images to base64 for embedding in APIs, HTML, and configuration files.
- Optimizing for search engines? Read how to optimize images for SEO to ensure your decoded images are served with correct alt text, dimensions, and modern formats.
- Choosing the right format? Our image format comparison covers when to use JPEG, PNG, WebP, and AVIF.
Base64 gets images through text-based systems. Pixotter's conversion tools get them ready for the web — compressed, converted, and optimized, all processed locally in your browser. Drop your decoded images in, pick the output format, and download the result.
Try it yourself
Ready to convert formats? Drop your image and get results in seconds — free, instant, no signup. Your images never leave your browser.