← Back to Blog

Base64 Encode/Decode Images Online: Complete Guide

You have a PNG file and an API that only accepts JSON. Or an HTML email that must embed a logo without hosting it externally. Or a CSS file where you want to inline a small icon. All of these scenarios lead you to the same place: Base64 image encoding. This guide covers exactly what it is, how it works, when to use it, and how to do it in JavaScript, Python, and the command line.

Why Developers Search for Base64 Image Encoding

The most common reason developers end up looking for a Base64 image converter is a constraint in the system they are integrating with. REST APIs that only accept JSON payloads cannot transport binary image data directly - JSON has no binary type. Email clients are notoriously restrictive about external image references. Embedded HTML reports, PDF generators, and single-file exports all benefit from having images self-contained rather than dependent on external URLs.

The second common reason is the opposite direction: someone sends you a Base64 string and you need to turn it back into a viewable image file. This happens when debugging APIs, inspecting stored data in databases, or extracting assets from a JSON payload.

In both cases, the underlying mechanism is the same: a binary-to-text encoding scheme called Base64.

What Is Base64?

Base64 is an encoding scheme that converts binary data into a string of printable ASCII characters. It works by taking 3 bytes of binary data (24 bits) at a time and splitting them into 4 groups of 6 bits each. Each 6-bit group is then mapped to one of 64 characters: A-Z, a-z, 0-9, +, and /. A = character is used as padding when the input length is not divisible by 3.

The encoding is completely lossless and reversible. Every binary file - PNG, JPEG, GIF, WebP, SVG - can be Base64-encoded and decoded back to the exact original bytes. There is no quality loss, no compression, and no modification to the underlying image data.

Base64 is an encoding, not an encryption. Anyone with the Base64 string can decode it back to the original image. Do not use Base64 to "hide" images.

The Data URI Syntax

When a Base64-encoded image is embedded directly in HTML or CSS, it uses a data URI (also called a data URL). The format is standardized in RFC 2397:

data:[mediatype][;base64],[data]

For common image types, this looks like:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==

data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/...

data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7

data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PC9zdmc+

data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoBAAEAAUAmJbACdAEO/gHOAAA=

The mediatype tells the browser how to interpret the data. For SVG, you can use either image/svg+xml with Base64, or skip Base64 entirely and URL-encode the raw SVG text, which produces a shorter result for text-heavy SVG files.

When to Use Base64 Image Encoding

Base64 is the right choice in these scenarios:

  • HTML email templates: Many email clients block external image requests or strip <img src="https://..."> tags. Embedding images as data URIs guarantees they always display.
  • JSON APIs: When an endpoint only accepts JSON and you need to transport an image (e.g., uploading a profile photo via a REST API), Base64 is the standard approach.
  • CSS icons: Small icons and UI elements (typically under 5 KB) can be inlined as Base64 data URIs in CSS background-image properties, eliminating an HTTP request.
  • Single-file exports: HTML reports, Markdown-to-PDF pipelines, or offline documents that must be self-contained with no external dependencies.
  • Canvas operations: The browser Canvas API's toDataURL() method returns images as Base64 data URIs, which is the standard way to export canvas content.
  • Storing images in databases: Some systems store small thumbnails or avatars directly in a database column as Base64 text.

When NOT to Use Base64 Images

Base64 has a significant downside: it increases the encoded size by approximately 33%. A 100 KB PNG becomes roughly 133 KB as a Base64 string. This happens because every 3 bytes of binary data expand to 4 ASCII characters.

Avoid Base64 for:

  • Large images: Any image over ~10 KB should be served as a separate file and cached by the browser. Inlining a 500 KB hero image as Base64 will dramatically slow down page parsing.
  • Images that are reused: A Base64 image embedded in HTML or CSS cannot be cached separately by the browser. If the same image appears on multiple pages, a separate URL lets the browser cache it once.
  • Images served over HTTP/2: HTTP/2 multiplexes multiple requests over a single connection, so the "save an HTTP request" argument for Base64 CSS icons is much weaker than it was under HTTP/1.1.

How to Encode an Image to Base64

JavaScript (Browser)

The most reliable browser approach uses the FileReader API, which handles the binary-to-Base64 conversion natively:

function imageToBase64(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result);
    // reader.result is the full data URI:
    // "data:image/png;base64,iVBORw0KGgo..."
    reader.onerror = reject;
    reader.readAsDataURL(file);
  });
}

// Usage with a file input element
document.getElementById('fileInput').addEventListener('change', async (e) => {
  const file = e.target.files[0];
  const dataUri = await imageToBase64(file);
  console.log(dataUri); // data:image/png;base64,...

  // To get just the Base64 string without the prefix:
  const base64Only = dataUri.split(',')[1];
  console.log(base64Only); // iVBORw0KGgo...
});

JavaScript (Node.js)

In Node.js, the built-in fs module reads files as Buffers, which have a native toString('base64') method:

const fs = require('fs');
const path = require('path');

function imageToBase64(filePath) {
  const buffer = fs.readFileSync(filePath);
  const base64 = buffer.toString('base64');
  const ext = path.extname(filePath).slice(1).toLowerCase();

  // Map extension to MIME type
  const mimeTypes = {
    png: 'image/png',
    jpg: 'image/jpeg',
    jpeg: 'image/jpeg',
    gif: 'image/gif',
    webp: 'image/webp',
    svg: 'image/svg+xml'
  };

  const mime = mimeTypes[ext] || 'image/png';
  return `data:${mime};base64,${base64}`;
}

const dataUri = imageToBase64('./logo.png');
console.log(dataUri.substring(0, 80) + '...');
// data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...

Python

Python's standard library includes a base64 module that handles the encoding directly:

import base64
import mimetypes

def image_to_base64(file_path: str) -> str:
    """Read an image file and return a data URI string."""
    mime_type, _ = mimetypes.guess_type(file_path)
    if not mime_type:
        mime_type = 'image/png'

    with open(file_path, 'rb') as f:
        encoded = base64.b64encode(f.read()).decode('utf-8')

    return f'data:{mime_type};base64,{encoded}'

# Usage
data_uri = image_to_base64('logo.png')
print(data_uri[:80])
# data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...

# Write data URI to a text file for embedding
with open('logo_base64.txt', 'w') as f:
    f.write(data_uri)

Command Line (Linux / macOS)

The base64 command is available on all Unix-like systems. Combine it with a printf to build the full data URI:

# Encode a PNG to Base64 (no line wrapping with -w 0 on Linux)
base64 -w 0 logo.png

# On macOS (no -w flag needed, but output has no newlines by default)
base64 logo.png | tr -d '\n'

# Build the full data URI in one command
echo "data:image/png;base64,$(base64 -w 0 logo.png)"

# Save to a file
echo "data:image/png;base64,$(base64 -w 0 logo.png)" > logo_base64.txt

How to Decode a Base64 String Back to an Image

JavaScript (Browser)

If you have a data URI, you can display it directly in an <img> tag or convert it to a downloadable Blob:

// Display directly in an img tag
document.getElementById('preview').src = dataUri;

// Convert data URI to a Blob for downloading
function dataUriToBlob(dataUri) {
  const [header, data] = dataUri.split(',');
  const mime = header.match(/:(.*?);/)[1];
  const binary = atob(data);
  const bytes = new Uint8Array(binary.length);
  for (let i = 0; i < binary.length; i++) {
    bytes[i] = binary.charCodeAt(i);
  }
  return new Blob([bytes], { type: mime });
}

// Trigger a file download
function downloadBase64Image(dataUri, filename) {
  const blob = dataUriToBlob(dataUri);
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  a.click();
  URL.revokeObjectURL(url);
}

downloadBase64Image(dataUri, 'decoded-image.png');

Python

import base64

def base64_to_image(data_uri: str, output_path: str) -> None:
    """Decode a Base64 data URI and write the image to disk."""
    # Strip the data URI prefix if present
    if ',' in data_uri:
        data_uri = data_uri.split(',')[1]

    image_bytes = base64.b64decode(data_uri)

    with open(output_path, 'wb') as f:
        f.write(image_bytes)
    print(f'Saved {len(image_bytes):,} bytes to {output_path}')

# Usage
base64_to_image(data_uri_string, 'decoded_output.png')

Command Line

# Decode a Base64 file back to binary on Linux
base64 -d logo_base64.txt > decoded_logo.png

# On macOS
base64 -D -i logo_base64.txt -o decoded_logo.png

# If the file contains the data URI prefix, strip it first
sed 's/^data:image\/[^;]*;base64,//' logo_base64.txt | base64 -d > decoded_logo.png

Encode or Decode Images Instantly

Use our free browser based tools - 100% client side, no file is ever uploaded to a server.

Image to Base64    Base64 Decoder

Performance Tradeoffs: The 33% Size Tax

The size increase from Base64 encoding is a direct consequence of how it works. Every 3 bytes of input become 4 output characters - a ratio of 4:3 or approximately 33.3% overhead. For a 90 KB JPEG, the Base64 string will be around 120 KB. For a 1 MB PNG, expect roughly 1.33 MB of Base64 text.

This has concrete implications:

  • Parse time: Large Base64 strings embedded in HTML block the parser. The browser must process the entire string before it can continue rendering.
  • Memory: JavaScript strings are stored in UTF-16 in V8 (Node.js/Chrome). A 1 MB Base64 string occupies 2 MB of heap memory before any decoding occurs.
  • Gzip compression: Base64 output is highly compressible because it uses only 64 distinct characters. When served over HTTP with gzip or Brotli, the 33% overhead is largely recovered - Base64-encoded data often compresses back to near its original binary size. This is why Base64 in API JSON payloads is more bandwidth-efficient than it initially appears.
  • No caching: A Base64 image inlined in HTML is downloaded with the page every time. A separate image URL can be cached by the browser indefinitely.

Browser Support

Data URIs are supported in all modern browsers and have been for over a decade. There are a few edge cases to be aware of:

  • Internet Explorer 8: Supported data URIs but with a 32 KB size limit. Not relevant for any active project in 2026.
  • Content Security Policy (CSP): A strict CSP with img-src 'self' will block data URI images. You must add img-src data: to your CSP if you use Base64 images inline.
  • SVG in CSS background-image: SVG data URIs work in all browsers, but Base64-encoding SVG is often unnecessary. URL-encoding the raw SVG text produces shorter output because SVG is mostly ASCII, and Base64 expands it by 33%.
  • HTML email clients: Most modern email clients (Gmail, Outlook, Apple Mail) support data URI images. Older enterprise Outlook versions may not. Test with your target audience.

Real Example: Embedding an Image in a JSON API Request

This is the most common practical use case: sending an image to a REST API endpoint that only accepts JSON.

// Browser: user selects a file via <input type="file">
async function uploadProfilePhoto(file) {
  const reader = new FileReader();
  const base64 = await new Promise((resolve) => {
    reader.onload = () => resolve(reader.result.split(',')[1]);
    reader.readAsDataURL(file);
  });

  const response = await fetch('/api/v1/profile/photo', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      filename: file.name,
      mimeType: file.type,
      data: base64          // Base64 string, no data URI prefix
    })
  });

  return response.json();
}

// Server side (Node.js): decode and save to disk
const { filename, mimeType, data } = req.body;
const buffer = Buffer.from(data, 'base64');
fs.writeFileSync(`./uploads/${filename}`, buffer);

Frequently Asked Questions

What is the difference between a Base64 string and a data URI?

A Base64 string is the raw encoded text - just the alphanumeric characters and symbols representing the binary data. A data URI is a complete URI scheme that wraps a Base64 string with a prefix describing the media type: data:image/png;base64,<base64string>. You need the data URI format to use the image directly in HTML or CSS. For API calls, you typically send just the Base64 string and specify the MIME type separately in a JSON field.

Does Base64 encoding compress images?

No. Base64 increases file size by approximately 33%. It is purely a text-safe encoding of binary data. It does not apply any compression. If you need to reduce image file size, use a proper image compressor first (lossy JPEG quality reduction or lossless PNG optimization), then encode the compressed result to Base64.

Can I use Base64 images in CSS?

Yes. Data URI images work in CSS background-image properties: background-image: url('data:image/png;base64,...');. This is a common technique for small icons and sprites to eliminate HTTP requests. However, be cautious with large images - the CSS file becomes very large and cannot take advantage of browser image caching separately from the stylesheet.

How do I check if a Base64 string is valid before decoding?

A valid Base64 string contains only the characters A-Z, a-z, 0-9, +, /, and = (for padding). Its length must be divisible by 4. In JavaScript: /^[A-Za-z0-9+/]*={0,2}$/.test(str) && str.length % 4 === 0. For data URIs, validate the prefix format before attempting to decode the payload.

Is it safe to store Base64 images in a database?

Technically yes, but it is generally not recommended for large images. Base64 text is ~33% larger than the raw binary, so storing it as a TEXT column wastes storage. Most databases have dedicated BLOB (Binary Large Object) column types for binary data that store the actual bytes without the Base64 overhead. Use Base64 in databases only for small thumbnails or when the database does not support BLOB types.

Why does my Base64 image look broken in HTML email?

The most common cause is line breaks in the Base64 string. Some Base64 encoders insert newline characters every 76 characters (the MIME standard). HTML email clients may reject or misinterpret Base64 strings with embedded newlines. Strip all whitespace from the Base64 string before embedding it in a data URI. In JavaScript: base64String.replace(/\s/g, ''). In Python: base64.b64encode(data).decode('ascii') does not add line breaks by default.

What is the maximum size for a Base64 image in a browser?

Modern browsers have no documented size limit for data URIs - the practical limit is available memory. However, browsers begin to show performance degradation with data URIs larger than ~1 MB. The recommendation from browser vendors is to keep inlined data URIs under 100 KB for images used in CSS, and under 5 KB for images in HTML. For anything larger, use a proper URL and rely on HTTP caching.

Use our free tools to encode and decode Base64 images instantly, entirely in your browser with no data sent to any server: Image to Base64 Converter and Base64 Encoder/Decoder.

UK
Written by Usman Khan
DevOps Engineer | MSc Cybersecurity | CEH | AWS Solutions Architect

Usman has 10+ years of experience securing enterprise infrastructure, managing high-traffic servers, and building zero-knowledge security tools. Read more about the author.