Images & Video

Base64 images: when inlining helps and when it costs you

A data URI embeds the picture in the markup. That is occasionally the right call and usually a way to make a page slower.

4 minute read

A data URI puts the image inside the document:

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...">

There is no separate file and no second request. The picture is part of the markup. That sounds like a straightforward win and it is a trade, so it is worth knowing what is on each side.

The 33% is not negotiable

Base64 represents binary data using 64 safe ASCII characters. Each character carries 6 bits, so three bytes of input become four characters of output. That is a fixed 33% expansion, plus padding, plus the data:image/png;base64, prefix.

A 40 KB PNG becomes about 54 KB of text. This is not a quality setting or an implementation detail, it is arithmetic, and no encoder avoids it.

Gzip claws some of it back, because base64 text is more compressible than the binary was, but not all of it. Assume you are paying real weight.

What you get for it

One fewer request. This mattered enormously under HTTP/1.1, where browsers allowed about six connections per host and every request queued. It matters far less under HTTP/2 and HTTP/3, where requests are multiplexed over one connection. A lot of "inline your images" advice is from the earlier era and has not been revisited.

No flash of missing image. The picture is present the moment the markup is parsed. For a logo above the fold, that can be the difference between a clean first paint and a visible pop-in.

A truly self-contained file. This is the strongest argument and the one that survives. An HTML file, an email signature or an exported report with inlined images is one file that works with no network, no relative paths and no broken-image icons.

What it costs

Caching stops working properly. This is the big one. A separate image file is cached once and reused across every page and every deploy. An image inlined into a stylesheet or a page is cached only as part of that file, so changing one line of CSS re-downloads every image in it.

It blocks rendering. A data URI in your CSS makes the stylesheet bigger, and the stylesheet is render-blocking. You have moved bytes from a resource the browser could fetch in parallel into one it must finish before painting.

No lazy loading. loading="lazy" cannot defer something that is already in the markup. Inline twelve product images and you have downloaded all twelve, including the ones nobody scrolls to.

The markup becomes unreadable. A few thousand characters of base64 in the middle of a template is a real cost to whoever maintains it.

A usable rule

Case Inline?
Icon under about 2 KB, used once Yes
Logo above the fold Maybe, measure it
Email signature image Yes, remote images are blocked by default
Single-file HTML report or export Yes, that is the point
Photograph No
Anything repeated across pages No, let it cache
Anything below the fold No, let it lazy load

The rough threshold people settle on is a couple of kilobytes. Above that, the caching loss outweighs the saved request.

Going the other way

Decoding is the more common everyday need. You are reading an API response, a saved email, a CSS file or a JSON payload and there is a wall of base64 that is supposedly an image. Running it through a Base64 to image decoder tells you immediately whether it is a valid image and what it actually depicts.

Two things that trip people up when decoding. The data:image/png;base64, prefix is part of the URI, not part of the base64, so a decoder expecting raw base64 needs it removed. And base64 from a URL or a JWT is often base64url, which uses - and _ in place of + and /; a standard decoder will reject it until those are swapped back.

Before you encode anything

Compress the image first. Encoding is the last step, not the first.

Inlining a 200 KB unoptimised PNG produces 270 KB of markup. Compressing it to 20 KB first produces 27 KB. The 33% penalty applies to whatever you hand it, so every byte you remove beforehand is removed with interest.

Use the encoder to inline small icons and check the resulting size, and the decoder to recover an image from a data URI you found in a stylesheet or an API response.

Further reading

Do it hereImage to Base64Encode any image file to a Base64 data URL stringOpen

Written for NextTool. Every statement about a tool is checked against that tool's code, and corrections are dated above. How guides are written

base64data-uriperformance