Upload a file
  1. base64hub
  2. Learn Base64
  3. Data URI performance
Performance

Data URI Performance: When Inlining Base64 Pays Off

Inlining a file as a Base64 data URI saves a request and costs bytes, caching and sometimes render time. Compression changes the maths, and it changes it differently for SVG.

What a data URI costs

A data URI puts a file inside a URL: data:image/png;base64,iVBORw0KGgo.... You can use it anywhere a URL goes, so an icon can live inside the HTML src attribute or a CSS background-image, and the browser never makes a separate request for it.

The trade-offs are:

  • Size. Base64 makes the payload 4/3 of the original, about 33% larger (exact formula in Base64 size overhead). The data:image/png;base64, prefix adds another 22 bytes.
  • Caching. The inlined file is cached only as part of the document that contains it. A logo inlined in every HTML page is downloaded again with every page. Inlined into a CSS file, it is cached with that CSS, and changing one icon invalidates the whole stylesheet.
  • Rendering. The browser cannot paint anything until render-blocking CSS has arrived. A stylesheet that carries 200 KB of inlined fonts or images delays first render by however long those bytes take to download. In HTML, inline data pushes the content after it further down the byte stream.
  • No lazy loading. loading="lazy" on an <img> with a data URI saves nothing, because the bytes already arrived with the page.

Against that, you save a request. Under HTTP/1.1 that was a big deal, since browsers opened only a handful of connections per host. With HTTP/2 and HTTP/3, many requests share one connection, so the saving is smaller: mostly the request and response headers plus one round trip of latency for files the browser discovers late.

Measured: what gzip and Brotli do to Base64

The 33% figure is for uncompressed text. Most HTML and CSS is served with gzip or Brotli, so the number that matters is the compressed size. We measured it ourselves.

Method. Sample files from Debian 13 packages: a 16×16 PNG icon (HighContrast icon theme), a 48×48 RGBA PNG (hicolor theme), the 2880×1800 Xfce blue wallpaper JPEG, an 800×500 JPEG resized from it with Pillow at quality 80, the Roboto Slab Regular WOFF2 font, and two SVG icons (a 479-byte Adwaita symbolic icon and the 7.6 KB GNOME Debian swirl). Each file and its Base64 encoding (payload only, without the data: prefix) was compressed with Node.js 24 zlib: gzip at level 9 and Brotli at quality 11, the settings typically used for pre-compressed static assets. Sizes are in bytes.

FileRawBase64Raw gzipBase64 gzipRaw BrotliBase64 Brotli
PNG 16×16281376285306285284
PNG 48×484,5526,0724,5754,6024,5514,589
JPEG 800×5008,19510,9287,4257,1737,1606,908
JPEG 2880×1800197,011262,684182,684183,945180,114181,140
WOFF2 font52,53270,04452,53852,76552,53652,690
SVG icon479640291427247410
SVG swirl7,61010,1483,1234,3982,5823,823

What the numbers show:

Already-compressed binaries lose almost all the overhead. PNG, JPEG and WOFF2 data is already compressed and close to random, so compressing it again gains little (both PNGs got slightly bigger under gzip). Base64 of random bytes uses 64 symbols at 8 bits each, and a Huffman coder brings that back to roughly 6 bits per symbol. Compressed Base64 landed between 96% and 107% of the compressed binary, and between 84% and 109% of the raw file. The JPEGs here were themselves slightly compressible, which is why their Base64 versions even came out a little smaller than gzipped JPEG; the difference is too small to plan around. The tiny 16×16 PNG shows gzip’s fixed 18 bytes of header and trailer as a large percentage, and Brotli’s lighter framing erased it.

SVG gets worse. SVG is text with lots of repetition, and compression loves it: the swirl shrank from 7,610 to 3,123 bytes with gzip. Base64 shuffles each 3-byte group into 4 characters whose values depend on alignment, so a repeated substring no longer looks the same twice. Gzipped Base64 SVG was 41–47% larger than gzipped SVG, and with Brotli 48–66% larger.

URL-encoded SVG vs Base64

SVG does not need Base64 at all, because it is already text. A URL-encoded data URI keeps it readable:

.flag {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3 0v16h2V8h3l1 2h5V2h-4L9 0z'/%3E%3C/svg%3E");
}

The minimum to escape is %, # (it would start a URL fragment), < and >, plus whichever quote character delimits the attribute. Swapping double quotes inside the SVG for single quotes avoids escaping them. Non-ASCII characters must be percent-encoded as UTF-8.

The same two SVGs, measured the same way:

SVGEncodingSizeGzipBrotli
IconBase64640427410
IconMinimal URL encoding493293253
IconencodeURIComponent723326279
SwirlBase6410,1484,3983,823
SwirlMinimal URL encoding7,1793,0572,549
SwirlencodeURIComponent11,5943,3192,750

“Minimal” means whitespace collapsed, double quotes switched to single, and only %, #, <, > escaped. It compressed to about the same size as the original SVG; the swirl is even slightly smaller because collapsing whitespace removed bytes. Full encodeURIComponent is larger uncompressed but still compresses far better than Base64. The SVG to data URI tool produces both forms side by side.

Base64 still makes sense for SVG when a tool or API only accepts Base64, or when the SVG contains characters you would rather not think about escaping.

Content Security Policy and data:

Under a Content Security Policy, 'self' does not include data: URLs. Each directive that should allow them must list the scheme:

Content-Security-Policy: default-src 'self'; img-src 'self' data:; font-src 'self' data:

Allowing data: in img-src and font-src is common and low risk. Allowing it in script-src, default-src or object-src is a different matter: it lets an attacker who can inject markup supply a script without needing any external host, which defeats much of the point of CSP. If inline images trip your policy, widen img-src only.

Browsers also block pages from navigating the top-level window to a data: URL, whether by link, redirect or script, so a data URI can be embedded in a page but cannot be opened as a top-level link.

When inlining is worth it

Usually worth it:

  • Tiny icons and UI images under roughly 1–2 KB, especially ones needed for first paint. The request overhead is a large fraction of their size, and compression erases most of the Base64 cost.
  • Single-file HTML. Offline reports, exported documentation, test coverage pages and HTML email previews that must work when opened from disk.
  • Critical CSS that is inlined in the HTML anyway, where a small background image travels with it.
  • Generated content such as a chart rendered to PNG in a script and embedded in an HTML report.

Usually not worth it:

  • Photos and anything above a few kilobytes. They are better as separate files with long cache lifetimes.
  • Images shared across many pages, which would be re-downloaded inside every HTML response.
  • Web fonts. A WOFF2 file is typically tens of kilobytes (the one measured above is 52 KB), inlining it into CSS makes the stylesheet render-blocking for longer, and separate font files can be cached and preloaded.
  • Anything below the fold. A separate file with loading="lazy" costs nothing until it is needed.

Email is its own case. Support for data: images varies between mail clients, so test in the clients your recipients actually use. The dependable way to embed an image in an HTML email is an inline MIME attachment referenced with cid:, which is still Base64, but inside the MIME structure. See Base64 in email.

Make a data URI from an image

Drop a PNG, JPEG, WebP or SVG and get the data URI and its size. The file stays in your browser.

Image to Base64

FAQ

Are Base64 images slower than normal images?

Not inherently. Small inline images avoid a request, while large ones bloat the HTML or CSS, cannot be cached separately and can delay rendering. Inlining helps below a few kilobytes and hurts above that.

Does gzip remove the 33% Base64 overhead?

For PNG, JPEG and WOFF2, almost entirely: in our measurements the gzipped Base64 ended up within a few percent of the binary file. For SVG and other text it does not, and gzipped Base64 SVG was 41–47% larger than gzipped SVG.

Which is better for inline SVG, URL encoding or Base64?

URL encoding. It is readable, editable, and compresses about as well as the original SVG. Escape at least #, %, < and >, and use single quotes inside the SVG if the attribute uses double quotes.

Why are my data URI images blocked by Content Security Policy?

CSP treats data: as a separate scheme that 'self' does not cover. Add data: to img-src (or font-src for fonts). Do not add it to script-src or default-src.

Keep reading