Every image tag you've ever written points somewhere: a file on your server, a CDN URL, a path relative to the page. The browser reads that pointer, makes a separate HTTP request, and waits for the response before it can paint the pixels. Convert an image to base64 and you skip that whole step. The image data itself becomes a long string of text, and that string gets dropped straight into the HTML or CSS where the image would normally be referenced. No second request, no separate file, no waiting on a network round trip that hasn't finished yet.
That sounds like a strict upgrade until you look at the numbers. Base64 is a text encoding, and text encodings of binary data are never free: this one costs roughly a third more bytes than the original file. So the question isn't really "should I convert images to base64," it's "for which images does skipping the request beat carrying the extra weight." That's a narrower question than it first appears, and most images fail it badly.
What a base64 image string actually is
Base64 is a way of representing arbitrary binary data using only 64 printable ASCII characters — the letters A through Z, a through z, the digits 0 through 9, plus "+" and "/". It takes every three bytes of the original file and re-encodes them as four base64 characters. Binary data can contain any byte value, including ones that break plain-text formats like HTML or JSON if inserted raw; base64 sidesteps that entirely by using only characters those formats already handle safely.
For images specifically, the encoded string gets wrapped in a data URI, which looks like this: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA.... Three parts make up that URI. The data: prefix tells the browser this isn't a normal URL, it's inline content. image/png is the MIME type, telling the browser exactly how to decode what follows — swap in image/jpeg, image/webp, or image/svg+xml depending on the source file, and getting this wrong is one of the more common reasons an embedded image just shows up broken. After the comma comes the actual base64-encoded image bytes, which can run from a few dozen characters for a tiny icon to hundreds of thousands for anything larger.
You can drop that full string anywhere a browser expects an image reference: the src attribute of an <img> tag, a CSS background-image: url(...) declaration, or even a field inside a JSON payload if you're shipping image data through an API. The browser decodes it on the spot and renders it exactly like it would a normal file, because as far as the rendering engine is concerned, it is one.
Where this genuinely helps
The clearest case is email HTML. Most email clients block or strip externally-hosted images by default, forcing recipients to click "load images" before they see anything beyond a wall of alt text. A base64-embedded image sidesteps that filter, since there's no external request for the client to block in the first place — the image data already arrived inside the message body. This is why a lot of transactional email templates inline their logos and small graphics rather than linking them.
Tiny, frequently-reused UI elements are the other good fit: a hamburger menu icon, a small arrow, a single-color logo mark that shows up in the header of every page. Each of these would otherwise be its own separate file, and separate files mean separate HTTP requests. Inlining a handful of small icons directly into your CSS as background images removes those requests entirely, which matters more than it might sound like on a page that's already making dozens of other requests for scripts, fonts, and stylesheets.
There's also a structural parallel worth noting here: base64 shows up constantly in web development well beyond images, and one of the more common places is inside a JWT, where the header and payload are base64url-encoded JSON. If you want the fundamentals of the encoding itself — how it works, where else it turns up, what it isn't (it is not encryption) — that's covered separately in our post on what base64 encoding actually is. This post sticks to the image-specific tradeoffs.
The tradeoff nobody mentions until it bites them
Two costs work against you when you inline an image, and they don't show up until the page is already live and someone's wondering why it feels sluggish. The first is the size penalty itself: base64 encoding turns every three bytes of the original file into four bytes of text, an overhead of about 33%. A 30KB PNG becomes roughly 40KB once encoded. That's not a rounding error on a page that already has a lot competing for bandwidth.
The second cost is the one that actually matters more in practice: caching. A normal image file, referenced by URL, gets cached by the browser independently of the page that requested it. Visit the page again, or visit a different page on the same site that reuses the same logo, and the browser serves the image straight from its local cache — no network trip at all. A base64 image has no such life of its own. It's just text sitting inside the HTML or CSS document, so it lives and dies with that document's own cache lifetime. Every time the containing page or stylesheet is reloaded, the image data gets re-downloaded along with it, even if the image itself hasn't changed since the last visit. Use the same icon inlined across ten different pages and you've paid for that icon's bytes ten separate times instead of once.
Here's the number that tends to convince people: for a typical page over HTTP/2, a separate request for a small file carries on the order of a few hundred bytes of overhead once you account for TLS and header costs, even with connection reuse and header compression already in play. Below roughly 1–2KB, base64's 33% tax is smaller than what a fresh request would have cost anyway, so inlining wins outright. Past that point the math flips fast, and by the time you're looking at a 10KB icon, you're paying over 3KB of pure encoding overhead for a request that would have cost a few hundred bytes to begin with — and you've thrown away caching on top of it. That's not a hard rule so much as a rough threshold, since actual server latency and connection state shift the exact crossover, but it's a useful gut check before you inline anything.
None of this touches the size of the image itself. Base64 encoding doesn't compress anything; it strictly adds bytes on top of whatever the source file already weighs. If a JPEG or PNG is larger than it needs to be, inlining it as base64 makes that problem worse, not better, since you're now paying the encoding tax on top of an already-bloated file.
Try it
GlaeKit's Image to Base64 Converter turns any image file into a ready-to-paste data URI, right in your browser. Nothing is uploaded to a server.
Frequently asked questions
Does embedding a base64 image hurt page performance?
It can, mostly through the caching loss rather than the size increase itself. A large or frequently-reused image inlined as base64 gets re-downloaded on every page load instead of being served from cache, which adds up fast across a multi-page site. For a single small icon that appears once, the effect is negligible.
Is there a size limit for base64-encoding an image?
Not a technical one enforced by the format itself, but a practical one. Browsers and email clients can handle large data URIs, though very large ones can slow down HTML parsing and bloat the document itself. As a rule of thumb, this technique stops paying off well before you'd hit any hard limit — usually somewhere in the low single-digit kilobytes.
Can I use a base64 image as a CSS background-image?
Yes. Paste the full data URI as the argument to url(): background-image: url("data:image/png;base64,..."). It works the same as referencing an external file.
Why doesn't the browser cache a base64 image separately?
Because it isn't a separate resource. A base64 image is just a string of text sitting inside the HTML or CSS file, so the browser's cache treats it as part of that document rather than as its own fetchable asset. It gets cached only to the extent the containing page or stylesheet does, and re-downloaded whenever that document is.
What MIME type should I use in the data URI?
Match it to the actual file: image/png for PNG, image/jpeg for JPG, image/webp for WebP, image/svg+xml for SVG, and so on. A mismatched MIME type is one of the most common reasons an embedded image fails to render even though the base64 data itself is valid.