I inherited a codebase where the previous developer had Base64-encoded every image on the marketing site β hero banners, team photos, product screenshots. The CSS file was 4.2MB of mostly Base64 strings. The site scored 9/100 on PageSpeed. Re-exporting every image as an external URL took two hours. The PageSpeed score hit 78 the same day. Base64 is a legitimate tool for specific situations. Applied broadly, it is a performance disaster.
Convert images to Base64 β or back to binary β locally.
Encode or decode in your browser. No upload. Your files stay on your device.
Open Base64 Converter βWhat Base64 Actually Is
Base64 is an encoding scheme defined inIETF RFC 4648that represents binary data as a string of 64 ASCII characters (AβZ, aβz, 0β9, +, /). It exists because many text-based protocols β HTTP headers, JSON, HTML attributes, CSS properties β are designed to carry text, not raw binary bytes.
Base64 allows binary files (images, fonts, PDFs) to travel through text-only channels without corruption. The trade-off is size. The encoding maps every 3 bytes to 4 characters:
// The 33.33% size increase is mathematical, not optional // 3 bytes in β 4 characters out, always // Example: 3 bytes (0xFF, 0xD8, 0xFF) β start of a JPEG // Base64 output: "/9j/" // For a 150KB JPEG image: // Binary size: 150,000 bytes // Base64 size: 150,000 Γ (4/3) = 200,000 bytes (200KB) // Overhead: +50,000 bytes (+33.33%) // This overhead is unavoidable in all Base64 variants // (standard, URL-safe, MIME) β the ratio is fixed by the math
Data URI Syntax for Images
A Base64-encoded image is embedded in HTML or CSS as a data URI:
<!-- HTML: inline Base64 image -->
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAY..." />
/* CSS: inline Base64 background */
.spinner {
background-image: url("data:image/gif;base64,R0lGODlhEAAQAMQAAORH...");
}
/* Converting an image file to Base64 in JavaScript */
async function imageToBase64(file: File): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result as string);
reader.onerror = reject;
reader.readAsDataURL(file);
// Result format: "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAA..."
});
}
/* Stripping the data URI prefix to get raw Base64 */
function getBase64String(dataUri: string): string {
return dataUri.split(',')[1]; // Everything after the comma
}The Real Performance Cost
The 33% size penalty is only part of the problem. Base64 images break browser caching.
When an image is served at a URL (/images/logo.webp), the browser caches it independently. Every page on your site that uses that image shares one cache entry. The image downloads once and is served from memory for the lifetime of the cache.
A Base64 image embedded in HTML is part of the HTML document. It re-downloads every time the page re-downloads. There is no separate cache entry. Ten pages using the same Base64 logo means ten separate downloads of that image data β one per page view.
| Factor | External URL Image | Base64 Inline Image |
|---|---|---|
| File size | Original size | +33% larger |
| Browser cache | Cached independently | No independent cache |
| HTTP requests | 1 additional request | 0 additional requests |
| CDN delivery | Edge-cached globally | Not CDN-cacheable |
| Parse time | Normal | HTML/CSS parser must decode before rendering |
| First page load | Slightly slower (extra request) | Slightly faster (no round-trip) |
| Subsequent page loads | Near-instant (cached) | Same as first load β no cache |
When Base64 Actually Makes Sense
Base64 is genuinely useful in four specific situations:
Email HTML:Email clients frequently block external URLs by default. Gmail shows a βShow imagesβ prompt that many users ignore. Inlining critical images as Base64 ensures they display regardless of the client's image-blocking policy.
Tiny critical assets under 1KB: A 16px loading spinner as a GIF is roughly 300β500 bytes. The single HTTP round-trip to fetch it at first load may cost more latency than the 33% Base64 size penalty. Webpack and Vite automatically inline assets below a configurable size threshold for this reason.
CSS fonts loaded in style blocks: A small woff2 font subset encoded in Base64 can eliminate a render-blocking font request. Google Fonts uses this technique for critical font faces.
Self-contained HTML files: When you need a single portable HTML file with no external dependencies β for offline documentation, local tools, or single-file exports β Base64 embedding makes external assets unnecessary.
Things That Go Wrong with Base64 Images
Large Base64 Strings in CSS Delay Rendering
CSS is render-blocking. A browser cannot display any page content until it has downloaded, parsed, and applied all CSS. A CSS file containing 200KB of Base64 image strings delays the first paint by the time it takes to download and parse all of that β even for images that are not visible on the first screen.
Extract inline Base64 images from critical-path CSS. If they must be inline, move them to a non-render-blocking stylesheet loaded with rel="preload".
Content Security Policy Blocks Inline Data URIs
Strict Content Security Policies (CSP) block data URIs in img srcattributes by default. You need to explicitly allow them:
# CSP header β allows data URIs in img elements only Content-Security-Policy: img-src 'self' data:; # Note: This does NOT allow data: in script-src or object-src # Allowing data: in script-src is a security vulnerability β do not do it
Webpack and Vite Inline Size Thresholds
Both Webpack and Vite automatically inline small assets as Base64 when they fall below a size threshold. The default is 4KB in Webpack (via url-loader) and 4KB in Vite. You can tune this:
// vite.config.ts β adjust the Base64 inline threshold
import { defineConfig } from 'vite';
export default defineConfig({
build: {
assetsInlineLimit: 1024, // Only inline if < 1KB (default is 4096)
// Lowering this prevents accidental inlining of images you want cached
},
});SVG Inline Is Not the Same as Base64
Inlining SVG markup directly in HTML (<svg>...</svg>) is not Base64 encoding. SVG is already text β no encoding needed. Inline SVG avoids the 33% size penalty entirely and allows CSS/JavaScript interaction with SVG elements. For icons and logos, inline SVG is almost always better than Base64-encoded PNG.
The Practical Rule
Under 1KB: consider Base64 if it eliminates a high-latency HTTP round-trip. Over 2KB: use an external URL. Always.
For production sites using Webpack or Vite, set the inline threshold to 1024 bytes and let the bundler handle it automatically. Do not hand-encode images unless you are building email templates or a self-contained offline HTML tool.
If you need to understand image formats before encoding them, our image compression guide covers lossy vs lossless and which format to pick for small assets. For understanding the broader privacy case for local file processing (why keeping files off servers matters), our guide on unsafe file uploads covers the risks.
Encode or decode Base64 locally.
Convert images to Base64 data URIs or decode Base64 back to files β all in your browser.
Open Base64 Converter β