Image to Base64 Converter

Convert images to Base64 encoded strings

Drop image here or click to select

The Exact Size Math: Base64 Is a 33% Tax You Cannot Compress Away

Base64 maps every 3 input bytes onto 4 output characters, so the encoded length is exactly 4 × ceil(n / 3), padded with up to two trailing = signs. That is a fixed +33.3% before anything else: a 30 KB PNG becomes roughly 40 KB of text, and a 750 KB photo becomes a full megabyte. The alphabet is A–Z, a–z, 0–9, + and / (RFC 4648); note that + and / are unsafe in URLs and query strings, which is why the base64url variant swaps them for - and _ — pasting a standard-alphabet string into a URL parameter is a classic corruption source. The tax is worse than it looks because transport compression barely helps. PNG, JPEG and WebP payloads are already compressed, so their bytes are statistically close to random; after Base64 they are still random, just spread across a 64-character alphabet. Gzip or brotli applied to the surrounding HTML typically claws back only 2–5% of the encoded image — compare that with the 70–80% ratios common for ordinary HTML, CSS or JavaScript text. In other words, a 100 KB image inlined into a compressed page still costs roughly 128 KB on the wire. The one overhead you can ignore is the prefix: data:image/png;base64, adds about 22 characters. It is the 33% that matters, and it is paid again on every single page view that includes the markup.
const encodedSize = (bytes) => 4 * Math.ceil(bytes / 3);

encodedSize(30 * 1024);   // 30 KB PNG   -> 40 960 chars (+33.3%)
encodedSize(750 * 1024);  // 750 KB JPEG -> ~1 MB of text

// gzip/brotli barely help: compressed image bytes remain
// statistically random after Base64 — only ~2–5% comes back

What You Give Up When You Inline: Caching, Lazy Loading and the Preload Scanner

A hosted image file is cached independently of your HTML: change one sentence of copy and returning visitors re-download the sentence, not the logo. An inlined image is fused to the document, so it rides along with every HTML response and gets invalidated by every deploy. Across pages the loss compounds — a data URL repeated in ten templates is downloaded ten times, where a hosted file with a long Cache-Control max-age is fetched once. Lazy loading quietly stops working too. loading="lazy" defers the network fetch of an image, but an inlined image has no fetch to defer — its bytes sit in the document and are transferred before anything below them, effectively turning a below-the-fold image into render-blocking payload. You also lose the browser's preload scanner, which discovers <img> URLs during HTML parsing and fetches them in parallel, and you lose CDN-side image optimization: format negotiation (AVIF/WebP per Accept header), resizing and quality tuning all need an addressable URL. The historical argument for inlining — saving an HTTP request — died with HTTP/1.1. Over HTTP/2 and HTTP/3, requests on a warm connection are multiplexed and cost a few dozen bytes of frame overhead, not a round trip. What remains is a narrow sweet spot: assets under roughly 2–5 KB that render on first paint, like one or two UI icons or a blurred LQIP placeholder that holds space while the real photo loads.

data: URL Anatomy (RFC 2397) — and Why SVG Should Skip Base64

The grammar from RFC 2397 is data:[mediatype][;base64],<data>. Every part except the comma and the payload is optional, and the defaults are the trap: omit the media type and the browser assumes text/plain;charset=US-ASCII, so a perfectly valid Base64 PNG renders as a broken-image icon because it was interpreted as plain text. Always spell out data:image/png;base64, in full — this tool emits it for you. SVG deserves special treatment. Because SVG is text, Base64 is the wrong container for it: you pay the 33% expansion and you destroy gzip's ability to find repeated XML patterns inside the page. The better encoding is a plain URL-encoded data URL: data:image/svg+xml,%3Csvg ... You only need to percent-encode the characters that break URL or CSS parsing — <, >, # and quotes; letters and most punctuation can stay literal. The result is usually 15–30% smaller than the Base64 version before compression, and dramatically smaller after, since the SVG source remains compressible text. The # rule matters doubly: fill="#f60" contains a raw #, which unencoded terminates the URL as a fragment — encode it as %23. One more charset trap: if the SVG contains non-ASCII text (Korean labels, arrows, emoji), a naive URL-encoded data URL may garble it. Either percent-encode those bytes as UTF-8 or fall back to Base64 for that one file.
/* Raster: Base64 is the only option */
.logo {
  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANS...");
}

/* SVG: URL-encode instead — smaller and gzip-friendly */
.icon {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2 2 22h20z' fill='%23f60'/%3E%3C/svg%3E");
}

/* Missing media type = text/plain assumed = broken image */
/* BAD:  data:;base64,iVBORw0KGgo... */

Where data: URLs Silently Fail: CSP, Gmail and Navigation Blocks

Content Security Policy is the first ambush. The data: scheme is not covered by 'self', so a policy like img-src 'self' blocks every inlined image on the page — often no broken-image icon, just empty space and a violation note buried in the console. If you inline images, your policy needs img-src 'self' data: explicitly, and security reviewers will rightly ask why, since data: in script-src or object-src is a genuine XSS vector. Grant it for images only. Email is the second. Gmail strips data: URIs from <img src> entirely, and most other clients (desktop Outlook, Yahoo) behave the same way — which is exactly backwards from what people expect, since "no external requests" sounds email-friendly. Icons in HTML email must either be hosted on an HTTPS URL or attached as CID-referenced inline attachments. Test in the real clients; the preview in your ESP is not the truth. Browsers also hardened navigation: since 2017 (Chrome 60, Firefox 59) a top-level navigation to a data: URL is blocked, because full-page data: documents were a popular phishing wrapper. Rendering inside <img>, CSS or fetch() still works fine. As for length: modern Chrome and Firefox handle multi-megabyte data URLs in src attributes and stylesheets without complaint, but URLs past a couple of megabytes become miserable to copy, diff or inspect in devtools — and legacy IE8 capped the entire URL at 32 KB, which is why old advice still cites that number.

Memory Costs, the atob() Unicode Trap, and Letting Your Bundler Decide

An inlined image is paid for three times in RAM. The browser holds the Base64 string in the document, the decoded file bytes, and — once the image is displayed — the decoded bitmap at width × height × 4 bytes. That last term dwarfs the others and ignores file size entirely: a 1000 × 1000 PNG occupies about 4 MB as a bitmap whether the file was 20 KB or 2 MB. Base64 baked into JavaScript bundles is the worst variant, because the string additionally goes through JS parsing and lives on the heap. If you encode in code rather than with this tool, mind the classic trap: atob() and btoa() operate on Latin-1 strings only. btoa() on any string containing non-Latin-1 characters throws InvalidCharacterError, and every Stack Overflow workaround built on unescape(encodeURIComponent(...)) is deprecated. For files, FileReader.readAsDataURL (what this page uses) is correct and never touches the problem; for byte arrays, the recently standardized Uint8Array.prototype.toBase64() and Uint8Array.fromBase64() finally handle binary properly. The decision rule condenses to three conditions: inline only if the file is under about 5 KB, it renders on first paint, and it is not reused across pages. Everything else should stay a hosted file. And note that your bundler already implements this rule: Vite inlines imported assets smaller than 4 KB by default (build.assetsInlineLimit: 4096), and webpack 5's asset modules default to 8 KB via parser.dataUrlCondition.maxSize. If you find yourself hand-inlining images your build tool chose to emit as files, the tool is usually right.
// vite.config.js — Vite already makes the inline/file call
export default {
  build: {
    assetsInlineLimit: 4096 // bytes; imports under 4 KB become data: URLs
  }
};

// webpack 5 equivalent (asset modules)
module.exports = {
  module: {
    rules: [{
      test: /\.png$/i,
      type: 'asset', // data URL under maxSize, separate file above
      parser: { dataUrlCondition: { maxSize: 8 * 1024 } }
    }]
  }
};
Last updated:

About this tool

An image-to-Base64 encoder turns a PNG, JPEG, GIF, SVG, or WebP file into a data: URL you can paste directly into HTML, CSS, or Markdown. Inlining small images this way removes a separate HTTP request — useful for icons in email templates, single-file HTML, or assets bundled inside a CSS sprite where caching is not critical.

How to use

  1. Drag an image onto the drop zone or click to choose a file from disk.
  2. Wait a moment while the file is read locally — nothing is uploaded to a server.
  3. Preview the image and inspect the generated data: URL or raw Base64 string.
  4. Copy the format you need (full data URL for HTML/CSS, raw Base64 for JSON payloads).
  5. Paste it into an <img src="…">, a CSS background-image: url(…), or a JSON field.

Common use cases

  • Inlining small icons in HTML email templates that block external image loading.
  • Embedding a logo in a single-file HTML report (no asset bundle).
  • Storing user-uploaded avatars in JSON when a binary upload pipeline is overkill.
  • Reducing HTTP requests for tiny critical-path images on a landing page.
  • Pasting an image into a Markdown file that needs to be self-contained.
  • Including an SVG favicon directly in <link rel="icon" href="data:…">.

Frequently asked questions

Q. How big should an image be before I should NOT inline it?

A. Rule of thumb: under ~5KB for icons. Larger than that you lose caching benefits — the browser cannot cache the image separately and re-downloads it with every page.

Q. Does Base64 increase image size?

A. Yes, by about 33%. SVG is the exception — for SVG you can often avoid Base64 entirely with data:image/svg+xml;utf8,…

Q. Will old browsers render data: URLs?

A. Every modern browser does. Some legacy email clients (older Outlook) strip data URLs in <img>; for email use, test with the actual client.

Q. Is the image uploaded to your server?

A. No. Encoding happens entirely in your browser via FileReader. The image bytes never leave your device.