← All articles 12 min read

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:


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

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.

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.