What Is Base64?
Base64 is a binary-to-text encoding scheme that represents binary data using only 64 printable ASCII characters: the uppercase letters A–Z, the lowercase letters a–z, the digits 0–9, and the two symbols + and /. A padding character = is appended when the input length is not a multiple of three bytes.
The name "Base64" comes directly from this character set size. Because every character in the set can be represented with exactly 6 bits (2⁶ = 64), Base64 encodes three bytes (24 bits) of binary data into four printable characters (4 × 6 bits = 24 bits), making it a lossless, reversible encoding.
Base64 was originally designed for email systems (MIME) that could only reliably transmit text data. Today it is used everywhere binary data must travel through text-only channels — from email attachments and JSON payloads to HTML data URIs and JWTs.
Why Encode Images to Base64?
The primary motivation is embedding binary image data directly inside a text document. A PNG or JPEG file is raw binary; you cannot paste it into an HTML file or a JSON object without encoding it first. Base64 solves this by producing a plain-text string that is safe to place anywhere text is accepted.
Key reasons developers reach for Base64 image encoding
- Eliminate HTTP round-trips — Inlining an image removes one network request entirely. For small icons or decorative elements this can shave meaningful time off the critical rendering path.
- Single-file distribution — Self-contained HTML files (reports, email templates, offline demos) carry all assets without external dependencies.
- Email HTML compatibility — Many email clients block external image requests for privacy reasons. Inline Base64 images bypass this restriction.
- API payloads — When submitting an image as part of a JSON body (e.g., a user-avatar upload endpoint), Base64 lets you encode the file into a string field.
- CSS backgrounds — Tiny data URIs embedded in stylesheets prevent extra requests for decorative sprites or icons.
- Content Security Policy — Inlined images are not subject to CSP
img-srchost restrictions, which can simplify policy configuration in constrained environments.
How Base64 Encoding Works
The algorithm step by step
Base64 processes input in 3-byte chunks (24 bits at a time):
- Take the next three bytes:
B1 B2 B3. - Concatenate their bits into a 24-bit string.
- Split the 24 bits into four 6-bit groups.
- Map each 6-bit value (0–63) to its Base64 character using the lookup table.
- Repeat until all bytes are consumed.
- Pad with
=characters if the final chunk has fewer than three bytes.
Example
The ASCII string Man (bytes 0x4D 0x61 0x6E) encodes as follows:
M a n
01001101 01100001 01101110 ← 24 bits
010011 010110 000101 101110
19 22 5 46
T W F u
Result: TWFu — three bytes became four characters.
The ~33% size increase explained
Four Base64 characters each carry 6 bits of information, totalling 24 bits. Those same 24 bits originally lived in three bytes (24 bits). The representation grows because each output character is stored as a full 8-bit ASCII byte, not 6 bits:
- Input: 3 bytes = 24 bits stored in 3 × 8 = 24 bits of storage.
- Output: 4 characters stored in 4 × 8 = 32 bits of storage.
- Overhead: 32 / 24 = 1.333… → 33.3% larger.
With gzip/Brotli compression applied, Base64-encoded text compresses very well (back toward original size), which partially mitigates the overhead in HTTP responses.
Data URIs Explained
A Data URI (also called a data URL) embeds file content directly in a URI string using the format defined in RFC 2397:
data:[<mediatype>][;base64],<data>
| Part | Description |
|---|---|
data: |
Scheme identifier |
<mediatype> |
MIME type (e.g. image/png, image/svg+xml) |
;base64 |
Indicates data is Base64-encoded (omit for plain text) |
,<data> |
The encoded (or plain) data payload |
Examples
PNG image in an <img> tag:
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA..." alt="icon">
SVG background in CSS:
.logo {
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0i...");
}
SVG without Base64 (URL-encoded):
SVG is already text, so it can be embedded without Base64 using percent-encoding:
.icon {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'...%3E%3C/svg%3E");
}
URL-encoding SVGs produces smaller output than Base64 for the same image.
Practical Code Examples
Browser: FileReader API
// Convert image file to Base64 in browser
function fileToBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
});
}
// Usage
const input = document.querySelector('input[type="file"]');
input.addEventListener('change', async (e) => {
const base64 = await fileToBase64(e.target.files[0]);
document.querySelector('img').src = base64;
});
reader.result already includes the full Data URI prefix (e.g. data:image/png;base64,...), so you can assign it directly to src.
Browser: Canvas API (resizing + encoding)
function resizeAndEncode(file, maxWidth = 200) {
return new Promise((resolve) => {
const img = new Image();
const url = URL.createObjectURL(file);
img.onload = () => {
const scale = Math.min(1, maxWidth / img.width);
const canvas = document.createElement('canvas');
canvas.width = img.width * scale;
canvas.height = img.height * scale;
canvas.getContext('2d').drawImage(img, 0, 0, canvas.width, canvas.height);
URL.revokeObjectURL(url);
resolve(canvas.toDataURL('image/webp', 0.85));
};
img.src = url;
});
}
JavaScript: Decode Base64 back to binary
// Decode a Base64 Data URI back to a Blob
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 });
}
Python: Encode and decode
import base64
# Encode image to Base64
with open("image.png", "rb") as f:
encoded = base64.b64encode(f.read()).decode("utf-8")
data_uri = f"data:image/png;base64,{encoded}"
print(data_uri[:80], "...") # Preview
# Decode Base64 back to image
image_data = base64.b64decode(encoded)
with open("output.png", "wb") as f:
f.write(image_data)
CSS: Embedding a small icon
/* Embedding a small icon in CSS */
.icon {
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0i...");
width: 24px;
height: 24px;
}
/* Alternatively, URL-encode small SVGs for better readability */
.icon-alt {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E...%3C/svg%3E");
}
Real-World Use Cases
1. Email templates
Email clients such as Outlook, Gmail (in some contexts), and corporate webmail often block external image loads by default. Embedding small images as Base64 data URIs in the HTML source ensures they always render, regardless of the client's privacy settings.
2. Progressive Web Apps (PWA) and offline-first apps
Inlining critical images into the HTML shell or service-worker-cached assets guarantees availability when the device is offline, without adding additional cache entries.
3. Open Graph / Social meta images (inline fallbacks)
Some server-rendered pages build inline fallback images for OG tags when the CDN asset may not yet be propagated.
4. Canvas-based image processing pipelines
canvas.toDataURL() is the standard output mechanism for in-browser image transformations — resizing, watermarking, format conversion — before uploading to a server.
5. Data export features
Generating downloadable reports (PDF, ZIP, HTML) that must carry their images without external hosting. A fully self-contained HTML invoice, for example, can be emailed or archived without worrying about dead image links.
6. CSS sprite replacement for tiny icons
Icon fonts and SVG sprites are common alternatives, but for one-off small icons a single Base64-encoded SVG background-image can be simpler to maintain.
Comparison: Embedded Base64 vs External File
| Factor | Base64 Embedded | External File |
|---|---|---|
| HTTP requests | 0 (inlined) | 1 per image |
| File size overhead | ~33% larger | None |
| Browser caching | Not cached separately | Cached by URL |
| CDN distribution | N/A | Full CDN support |
| Cache invalidation | Bust parent document | Change filename/hash |
| Best for | Small icons < 5 KB | Photos, large images |
| Email compatibility | Excellent | Often blocked |
| Offline support | Built-in | Requires service worker |
Performance Trade-offs and Considerations
When Base64 helps performance
- Tiny images (< 2–5 KB): The HTTP round-trip cost (DNS lookup + TCP handshake + TLS negotiation + request/response) for a small file often exceeds the 33% size penalty. Inlining wins.
- HTTP/1.1 environments: Browsers limit concurrent connections per host (typically 6). Reducing request count is more impactful here than under HTTP/2.
- Critical above-the-fold images: Inlining a hero icon or a logo means it renders with the first HTML byte — zero additional round trips.
When Base64 hurts performance
- Large images (> 5–10 KB): The size overhead becomes significant. A 100 KB PNG becomes ~133 KB Base64. More bytes to download, more memory for decoding.
- Repeated images: An external image file is downloaded once and reused from cache across multiple pages. A Base64 string embedded in each page re-downloads with every page load.
- HTTP/2 multiplexing: Under HTTP/2, many small requests are multiplexed over a single connection at near-zero overhead. The "eliminate requests" argument weakens substantially.
- Document parse time: A large inline Base64 string contributes to HTML parse time and increases the DOM serialization cost.
- Browser cache: Data URIs embedded in HTML are cached only as part of the document. They do not get their own cache entry with independent expiry, so they cannot be shared across pages or reused without re-parsing the document.
Compression note
Base64-encoded text compresses very well with gzip/Brotli because it has high redundancy (limited character set, repeated patterns). When HTTP compression is enabled, the effective transfer size penalty drops to around 2–8% for typical images. This significantly improves the break-even point.
Best Practices
Set a size threshold. Inline images below 2–5 KB; use external URLs for anything larger. Many bundlers (webpack, Vite) apply this rule automatically via
url-loader/asset/inlinewith a configurablelimit.Prefer SVG over raster for icons. SVGs are already text; they compress even better than raster Base64 and can be styled with CSS. Use URL-encoding (
encodeURIComponent) rather than Base64 for SVGs to keep them human-readable.Enable HTTP compression. Ensure gzip or Brotli is enabled on your server so the 33% overhead is mostly negated in transit.
Use WebP or AVIF. Modern formats produce much smaller files before encoding, making the Base64 output proportionally smaller too.
Audit with DevTools. Check the Network tab. If inlined images are making your HTML document significantly larger, consider moving them to external URLs with a proper CDN and long-lived cache headers.
For email, test across clients. Not all email clients render data URIs identically. Test in Outlook, Gmail (web and app), Apple Mail, and mobile clients before relying on inline images for critical email content.
Strip metadata before encoding. Run images through a tool that strips EXIF data before embedding — EXIF can add kilobytes of unnecessary data (and potentially expose sensitive GPS coordinates).
Use build-time tooling. Rather than manually encoding images, use webpack's
asset/inline, Vite's?inlineimport suffix, or PostCSSpostcss-inline-base64to automate the process reliably.
SVG vs Raster: A Note on Encoding
SVG images are XML text, which means they can be embedded in CSS without Base64 by simply URL-encoding the markup. This approach is preferred because:
- The result is smaller than Base64 for SVG content.
- The SVG remains human-readable inside the stylesheet.
- No decoding overhead.
/* URL-encoded SVG — preferred for SVGs */
.checkmark {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z'/%3E%3C/svg%3E");
}
/* Base64-encoded SVG — acceptable but less readable */
.checkmark-b64 {
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTkgMTYuMTdMNC44MyAxMmwtMS40MiAxLjQxTDkgMTkgMjEgN2wtMS40MS0xLjQxeiIvPjwvc3ZnPg==");
}
FAQ
Q1: What is the maximum size of a Data URI?
There is no hard standard limit, but browsers impose practical limits. Chrome and Firefox handle data URIs up to ~2 MB without issues. Internet Explorer historically limited data URIs to 32 KB. For production use, keep embedded images under 5 KB to avoid impacting document parse time.
Q2: Can I use Base64 images in CSS content: property?
Yes. Pseudo-elements accept data URIs in content: url(...):
.badge::before {
content: url("data:image/png;base64,...");
}
Note that content: url() renders the image at its intrinsic size and cannot be resized with width/height — use background-image instead for sizing control.
Q3: Does Base64 encoding affect image quality?
No. Base64 is a lossless encoding. It encodes and decodes the exact original bytes without any modification. The image quality is determined solely by the original image file and its format (lossy JPEG vs lossless PNG/WebP).
Q4: How do I decode a Base64 string back to a file?
In the browser:
const byteString = atob(base64String); // decode
In Python:
import base64
data = base64.b64decode(b64_string)
In Node.js:
const buf = Buffer.from(b64String, 'base64');
Q5: Are Base64 images indexed by search engines?
Search engines can index images delivered via data URIs, but they may not crawl them as efficiently as externally-hosted images. For SEO-critical images, host them externally with descriptive filenames, proper alt text, and structured data markup.
Q6: Can Base64-encoded images be used in <canvas>?
Yes. You can set image.src = dataURI on an HTMLImageElement and then ctx.drawImage(image, ...) to render it on a canvas, just as with any image URL.
Q7: Why does my Base64 string end with ==?
Padding characters (=) are added when the input byte count is not divisible by 3. One = means the last chunk had 2 bytes (16 bits → 18 bits encoded → 1 padding character). Two == means the last chunk had 1 byte (8 bits → 12 bits encoded → 2 padding characters).
Q8: Is Base64 a form of encryption?
No. Base64 is purely an encoding scheme, not encryption. It provides no confidentiality — anyone can decode it instantly. Do not use Base64 to "hide" sensitive data. For security, use actual encryption (AES, RSA, etc.).