Our marketing site shipped a new homepage, and Lighthouse performance dropped by 11 points. The culprit wasn't a giant hero image — it was 14 tiny icons inlined as base64 data URLs, each one a 2KB PNG turned into a 2.7KB string, plus 14 copies of the same base64 SVG scattered through the markup. Nobody noticed until the build engineer asked why the HTML was 80KB.
Inlining an image as base64 means the browser needs no extra request to show it. That genuinely helps for:
• A favicon or small logo used once, where saving a request matters on a slow connection.
• Email HTML, where external images are often blocked — a data URL displays without a server round-trip.
• Single-file prototypes you can email or paste into a code review without hosting assets.
For something like a 3KB logo, the 33% size penalty is a fine trade for zero requests.
Base64 inflates binary by roughly 33%: every 3 bytes of input become 4 characters of output. That ratio is unforgiving at scale. A 100KB image becomes a 133KB string in your HTML — and now it's part of the HTML document itself, so it can't be cached separately, it re-downloads on every page load, and it bloats your FCP.
// 3 bytes → 4 chars. No exceptions.
// 10 KB image → ~13.3 KB string
// 500 KB image → ~666 KB string — don't.
My hard rule: embed only if the original file is under ~5KB. Above that, a real file with proper caching wins almost every time.
When an icon genuinely earns inlining, I don't hand-edit. I use the image-to-base64 tool, drop the file in, and get a ready-to-paste data:image/…;base64,… string — all local, nothing uploaded. For SVG specifically, I check whether the source is small enough to inline the raw SVG instead of base64, which is even smaller and stays editable.
The 33% expansion is a well-documented property of the encoding; the Wikipedia page on Base64 walks through the math if you want the full derivation.