Embedding images as Base64 in HTML/CSS: pros and cons
Data URIs let you inline images directly into HTML and CSS. Sometimes that's brilliant, sometimes it's terrible. Here's how to tell the difference.
There’s a technique in web development where, instead of linking to an image file with <img src="logo.png">, you embed the actual image bytes directly into the HTML or CSS as a Base64 string:
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA..." />
This is called a data URI. It can make pages load faster or slower, bigger or smaller, and get you into a surprising amount of trouble if misused. Here’s when it’s a smart optimisation and when it’s a mistake.
What a data URI actually is
A data URI is a URL that contains the data itself, not a pointer to data somewhere else. The format is:
data:<mime-type>;base64,<base64-encoded-data>
For a PNG image:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...
For an SVG:
data:image/svg+xml;base64,PHN2ZyB4bWxucz0i...
Or, for SVG specifically, you can skip Base64 and use URL-encoded SVG text:
data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg'>...</svg>
The browser parses the URI, decodes the data, and renders the image as if it had been loaded from a file.
Why anyone would do this
The usual argument: it saves an HTTP request.
Without the data URI, the browser requests image.png — one HTTP request, one round-trip, then the image downloads. With the data URI, the image is already in the HTML. No extra request. Renders immediately once the HTML is parsed.
In the days before HTTP/2, every request had noticeable overhead. For a page with fifty tiny icons, that overhead added up. Inlining small images as data URIs could measurably speed up page rendering.
The arguments against
Data URIs bloat the file they’re embedded in
Base64 adds 33% overhead to the raw file size. Inlining a 1 KB icon adds roughly 1.3 KB to your HTML. For one icon, fine. For fifty, you’ve added 65 KB to your HTML payload — which has to be downloaded and parsed before anything renders.
They can’t be cached separately
A logo.png file requested once is cached by the browser and reused across every page of your site. A Base64-encoded logo is part of every single HTML page it appears on. First visit: same speed. Every subsequent page visit: wasted bandwidth.
They block parallel downloads
Browsers download multiple external assets in parallel. Inlined data URIs are part of the HTML itself, which means they compete with (and delay) the initial HTML download. If the HTML is 500 KB because of inlined images, the page appears blank for longer.
They’re ugly in source code
A 200-character Base64 string in the middle of your HTML or CSS makes code review and debugging harder. That’s enough reason for most developers to avoid them for anything but truly small assets.
HTTP/2 and HTTP/3 mostly eliminate the original benefit
The main argument for data URIs — “avoid extra HTTP requests” — was strong on HTTP/1.1, where each request had noticeable cost. HTTP/2 multiplexes many requests over one connection at almost zero per-request overhead. HTTP/3 goes further. On modern servers, twenty separate image requests are not meaningfully slower than one.
When data URIs are genuinely a good idea
Very small, very frequent icons
A 100-byte CSS icon (a checkmark or arrow) used many times on a single page is a good data URI candidate. The overhead is tiny, it renders instantly, and the saved HTTP request is worth more than the HTML bloat.
Background images in CSS used on every page
CSS files are cached long-term. A data URI inside a CSS file gets cached with the CSS, so the “no caching” objection doesn’t apply quite as strongly. For tiny background images — gradients you can’t do with CSS, texture overlays — data URIs in CSS can be cleaner than separate files.
Email HTML
Many email clients block remote images by default (“Display images?” banner). Embedding images as data URIs means they render regardless of the block — except that some email clients (Gmail, Outlook) also block data URIs, so test thoroughly before committing to this approach.
Offline-first web apps
If your app must function without a network connection, data URIs ensure that images bundled in the app always display. A real use case for installed web apps and Progressive Web Apps.
Generated images at runtime
If you generate an image dynamically in JavaScript — drawing on a <canvas> and needing to display or download the result — the natural output is a Base64 data URL. You don’t need to save to a file first.
Print stylesheets and isolated artifacts
HTML meant to be printed, emailed, or saved as a single-file document often benefits from data URIs because there are no separate files to worry about.
When data URIs are definitely the wrong choice
Large images. Anything above about 10 KB (13 KB Base64-encoded) is almost always better as a separate file. The HTML bloat is too high, the caching benefit matters too much.
Images that change. An avatar, a header photo, a product image — anything that varies. These should be files, cached and replaced as needed. Data URIs lock the image into the HTML.
SEO-visible images. Search engines can crawl data URIs but treat them as less significant for image search than proper files. If you want an image to rank in Google Images, use a proper file with good alt text.
Images referenced multiple times. If the same icon appears in twelve places, a data URI pays the encoding cost twelve times. A proper file is cached once and reused.
The break-even rule of thumb
For most modern websites on HTTP/2 or HTTP/3:
| Asset size | Used once | Used many times |
|---|---|---|
| < 1 KB | Data URI OK | Data URI OK |
| 1-5 KB | Data URI OK | Separate file |
| 5-10 KB | Marginal | Separate file |
| > 10 KB | Separate file | Separate file |
If unsure, benchmark. Tools like Chrome Lighthouse and WebPageTest measure the actual impact.
A practical workflow
If you decide a specific image is a good data URI candidate:
-
Optimise the image first. Run it through a compressor — a 10 KB PNG optimised down to 3 KB makes a much better data URI than the unoptimised version. See our Image Compressor.
-
Convert to Base64. Drop the file into a Base64 tool. You’ll get the Base64 string immediately.
-
Build the data URI. Prepend
data:image/png;base64,(or the matching MIME type) to the string. -
Paste into your HTML or CSS. For HTML:
<img src="data:...">. For CSS:background: url(data:...). -
Test in multiple browsers and email clients. Support is universal in modern browsers but varies in email.
A note on SVG
SVG is a special case. SVG images are text-based, which means they can be embedded in HTML or CSS as raw text, not Base64. Raw SVG is typically smaller than Base64-encoded SVG, because Base64 always adds 33% while raw SVG just needs minor escaping for special characters.
/* Base64-encoded SVG (larger) */
background: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3...');
/* URL-encoded SVG text (smaller, often by 40%+) */
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg">...');
For SVG specifically, prefer URL-encoded text over Base64.
A note on SEO and accessibility
alttext still works normally on<img>with data URIs — use it.- Right-click → save image in the browser works, but the saved filename will be generic.
- Search engine image crawling handles data URIs fine but prioritises separate files.
- Content Security Policy (CSP) rules may need explicit allowance for
data:URIs.
Data URIs are a precision tool. Used for the right asset in the right place, they can shave a request and speed up a page. Used indiscriminately, they bloat your HTML and waste bandwidth. If you want to experiment — to embed an image in an email template, or test a data URI before committing — the Base64 encoder handles it. Drop the file in, copy the string out, build the data URI. Everything runs locally, so even corporate logos or private assets stay on your device.