table of contents feature [open]

How to Convert an Image to Base64 (Data URI) for CSS & HTML

Diagram of an image file converted to a Base64 data URI string used in HTML and CSS code

Last reviewed: October 1, 2026

Short answer: To embed an image directly in HTML or CSS, Base64-encode the file's bytes and put the result in a data URI: data:image/png;base64,iVBORw0KGgo.... Use that string anywhere a URL is accepted, such as <img src> or background-image: url("..."). You can produce it with an online converter, the base64 command, PowerShell, FileReader.readAsDataURL() in JavaScript or the base64 module in Python.

Base64 turns every 3 bytes into 4 characters, so the encoded text is about one third larger than the file, and an inlined image cannot be cached on its own. Inline only small, critical images such as tiny icons and placeholders. Serve photos and anything reused across pages as normal files.

This guide explains how Base64 and data URIs work, shows every common way to use and create them, and sets out when inlining helps and when it hurts.

If you only need to convert a file or two, a free, browser-based image to Base64 converter is the quickest route, and the rest of this guide explains what to do with the string it gives you.

What Base64 is and how it works

Base64 is a way to write arbitrary bytes using only 64 printable ASCII characters. It is defined in RFC 4648. It is an encoding, not compression and not encryption: anyone can reverse it, and the output is larger than the input.

The alphabet is A-Z (values 0 to 25), a-z (26 to 51), 0-9 (52 to 61), then + (62) and / (63). A 65th character, =, is used only for padding. Each character carries 6 bits, so the encoder takes the input in 24-bit groups (3 bytes) and writes each group as 4 characters.

A worked example

Every PNG file starts with the bytes 89 50 4E (hexadecimal). Here is how those three bytes become four characters:

Bytes (hex):      89       50       4E
Bits:             10001001 01010000 01001110
Regroup by 6:     100010 010101 000001 001110
Decimal value:    34     21     1      14
Base64 character: i      V      B      O

The result is iVBO, which is why every Base64-encoded PNG begins with iVBORw0KGgo. You can recognize other formats the same way: JPEG data starts with /9j/ and GIF data starts with R0lGOD.

Padding

When the input length is not a multiple of 3, the last group is padded. RFC 4648 defines three cases: no padding when the final group is a full 3 bytes, two = characters when 1 byte is left over, and one = when 2 bytes are left over. Encoding the text "Man" one character shorter each time makes this easy to see:

"Man" (3 bytes) -> TWFu
"Ma"  (2 bytes) -> TWE=
"M"   (1 byte)  -> TQ==

The encoded length is therefore always a multiple of 4. RFC 4648 also defines a "URL and filename safe" variant, often called base64url, that replaces + with - and / with _. Data URIs with ;base64 use the standard alphabet, not base64url.

What a data URI is

A data URI (MDN calls it a data URL) is a URL that contains the file's content instead of pointing to it. RFC 2397 defines the syntax:

data:[<mediatype>][;base64],<data>
  • data: is the scheme.
  • <mediatype> is the MIME type, such as image/png. If you leave it out, it defaults to text/plain;charset=US-ASCII, which is wrong for an image.
  • ;base64 says the data is Base64-encoded. Without it, the data is treated as text and may be percent-encoded.
  • The comma separates the header from the data. It is required.

MIME types for common image formats

FormatMIME typeData URI prefix
PNGimage/pngdata:image/png;base64,
JPEGimage/jpegdata:image/jpeg;base64,
GIFimage/gifdata:image/gif;base64,
WebPimage/webpdata:image/webp;base64,
SVGimage/svg+xmldata:image/svg+xml;base64, or data:image/svg+xml,
ICOimage/vnd.microsoft.icon (official); image/x-icon is also widely useddata:image/x-icon;base64,

Note that the JPEG type is image/jpeg, never image/jpg, whatever the file extension is.

How to use a Base64 image

A data URI works wherever the browser expects an image URL. The examples below use a real, complete image: a 1 by 1 pixel transparent GIF that is 42 bytes long and 56 characters in Base64. Longer strings are cut short with ... to keep the samples readable.

In an HTML img element

<img
  src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
  width="1" height="1" alt="">

As a CSS background image

.icon-check {
  width: 16px;
  height: 16px;
  background-image: url("data:image/png;base64,iVBORw0KGgo...");
  background-repeat: no-repeat;
}

Keep the quotes inside url(). CSS allows an unquoted URL, but MDN notes that quotes become required when the URL contains characters such as parentheses, whitespace or quotes, and SVG data URIs usually do.

As a favicon

<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo...">

Inside an SVG

<svg xmlns="http://www.w3.org/2000/svg" width="200" height="100">
  <image href="data:image/jpeg;base64,/9j/4AAQ..." width="200" height="100"/>
</svg>

This matters because, as MDN explains, an SVG used as an image (for example in <img> or a CSS background) cannot load external resources. A raster image inside such an SVG has to be inlined as a data URI.

In JSON and API payloads

JSON has no binary type, so APIs that accept an image in a JSON body usually take Base64 text. Check the API's documentation for the exact shape. Some want the full data URI and some want only the Base64 part with the MIME type in a separate field:

{
  "filename": "avatar.png",
  "contentType": "image/png",
  "data": "iVBORw0KGgo..."
}

In HTML email

You can write <img src="data:..."> in an email, but do not rely on it. Email clients apply their own filtering to message HTML, and support for data URI images is inconsistent between clients. A hosted image URL, or an image attached to the message and referenced from the HTML, is the more dependable choice.

The size and performance cost

Base64 makes the image about 33 percent larger, and inlining removes the image's separate place in the HTTP cache. Those are the two costs to weigh against the saved request.

The arithmetic

Four characters come out for every three bytes that go in, and the final group is padded up to four characters. So for a file of n bytes:

encoded length = 4 * ceil(n / 3)
Original sizeBase64 lengthIncrease
1 byte4 characters300%
100 bytes136 characters36.00%
1,000 bytes1,336 characters33.60%
3,000 bytes4,000 characters33.33%
10,240 bytes (10 KiB)13,656 characters33.36%
1,048,576 bytes (1 MiB)1,398,104 characters33.33%

What gzip and Brotli change

HTTP compression recovers much of the overhead, but not all of it, and it never makes the image smaller than the original. Base64 text uses only 64 symbols, so a compressor can pack it back toward 6 bits per character. In a quick local test for this guide, 30,000 random bytes (a stand-in for already-compressed image data) became 40,000 Base64 characters, and gzip brought that text back down to 30,322 bytes, about 1 percent above the original. Your results will vary with the data and the compressor.

Two caveats apply. The browser still has to hold and decode the full-size string, and the saving exists only if the HTML or CSS response is actually served with gzip or Brotli. MDN recommends compression for text resources and advises against compressing files that are already compressed, such as images, so the normal image file needs no such step.

Caching, rendering and lazy loading

  • No separate caching. An inlined image is part of the document that contains it. web.dev points out that if the HTML is not cached, inlined resources are never cached, and that inlined resources are not shared between documents. Change one character of the page and the image is downloaded again.
  • Heavier CSS. Browsers treat stylesheets as render-blocking, so every Base64 image in a stylesheet adds bytes that must arrive before the page paints, even if that image is far down the page or never shown.
  • Slower discovery of other resources. web.dev notes that inlining too much delays the preload scanner from finding resources later in the document, because the extra inline content takes longer to download.
  • No real lazy loading. loading="lazy" defers the network request for an image. An inlined image has no request to defer: its bytes have already been downloaded with the page.

When inlining helps and when it hurts

Inline an image when saving one request is worth more than caching it separately. That is usually true only for very small images that are needed immediately.

Good candidates for a data URIPoor candidates
Tiny icons and UI glyphs used on one pagePhotos and other large images
A small blurred or solid placeholder shown before the real image loadsLogos and icons reused across many pages
Single-file documents: reports, demos, offline HTML, email-like exportsImages below the fold that could be lazy-loaded
Environments where a second request is impossible or awkwardImages that change on a different schedule from the page

The old argument for inlining was that each HTTP/1.1 request was expensive. That argument is much weaker today. MDN describes HTTP/2 as enabling full request and response multiplexing to reduce latency, and HTTP/3 as running over QUIC to reduce latency further. On those protocols many small files can share one connection, so a separate, cacheable image file usually costs little.

The SVG special case

SVG is text, so it does not need Base64 at all. Percent-encoding only the characters that are unsafe in a URL is usually smaller and stays readable.

Take this 110-character icon:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><circle cx="8" cy="8" r="7" fill="#1a73e8"/></svg>

Base64 turns it into 148 characters, for a 174-character data URI. The URL-encoded version below is 143 characters in total:

.dot {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='7' fill='%231a73e8'/%3E%3C/svg%3E");
}

The recipe is short:

  1. Use single quotes inside the SVG so they do not clash with the double quotes around the CSS URL.
  2. Encode < as %3C and > as %3E.
  3. Encode # as %23. An unencoded # starts a URL fragment and cuts the image off. This is the most common mistake with hex colors.
  4. Encode any literal % as %25.
  5. Keep the xmlns attribute. An SVG loaded as an image needs it.

How to convert an image to Base64

Every method does the same thing: read the file's raw bytes, Base64-encode them, and add the data:<type>;base64, prefix. Pick whichever fits your workflow.

Online tool

An online converter is the fastest way for a one-off job: choose the file and copy the string.

Linux and macOS command line

# GNU coreutils (Linux): -w 0 disables line wrapping
base64 -w 0 logo.png > logo.b64

# Build a complete data URI
printf 'data:image/png;base64,%s' "$(base64 -w 0 logo.png)" > logo.txt

GNU base64 wraps output at 76 characters by default, and the GNU manual says -w 0 turns wrapping off. The base64 command that ships with macOS takes different options, so check man base64 there; if your version has no -w flag, remove newlines with base64 < logo.png | tr -d '\n'.

Windows: PowerShell and certutil

# PowerShell: one line, no line breaks
$b64 = [Convert]::ToBase64String([IO.File]::ReadAllBytes("C:\img\logo.png"))
"data:image/png;base64," + $b64 | Set-Content -NoNewline logo.txt

Convert.ToBase64String with a single byte-array argument returns one unbroken string. Line breaks every 76 characters appear only if you pass Base64FormattingOptions.InsertLineBreaks.

certutil -encode logo.png logo.b64

Microsoft documents certutil -encode InFile OutFile as "Encodes a file to Base64". Be aware of the output format: in our test on Windows Server 2022 the file started with a -----BEGIN CERTIFICATE----- line, ended with -----END CERTIFICATE-----, and was wrapped at 64 characters. Remove those two lines and all line breaks before using it. PowerShell is the simpler option.

JavaScript in the browser

For a file the user picks, use FileReader.readAsDataURL(). Its result is a complete data URI:

const input = document.querySelector("input[type=file]");

input.addEventListener("change", () => {
  const file = input.files[0];
  if (!file) return;

  const reader = new FileReader();
  reader.addEventListener("load", () => {
    const dataUrl = reader.result;           // "data:image/png;base64,iVBORw0KGgo..."
    const base64 = dataUrl.split(",")[1];    // Base64 part only
    console.log(dataUrl.length, base64.length);
  });
  reader.readAsDataURL(file);
});

For pixels you have drawn, canvas.toDataURL(type, quality) returns a data URI. The default type is image/png. It throws a SecurityError if the canvas contains cross-origin content, and MDN recommends toBlob() with URL.createObjectURL() for larger images because toDataURL() holds the whole image in an in-memory string.

Be careful with btoa(). It expects a "binary string" in which every character stands for one byte, and it throws an InvalidCharacterError if any character has a code point above 255. It is therefore not a safe way to encode arbitrary text or the bytes of a fetched image without a conversion step. If you already have bytes in a Uint8Array, the newer toBase64() method does the job directly:

const response = await fetch("/img/logo.png");
const bytes = new Uint8Array(await response.arrayBuffer());
const dataUrl = "data:image/png;base64," + bytes.toBase64();

MDN lists Uint8Array.prototype.toBase64() as available in the latest browsers since September 2025, so use a polyfill or the FileReader route if you support older ones.

Python

import base64

with open("logo.png", "rb") as f:          # "rb": read bytes, not text
    encoded = base64.b64encode(f.read())   # returns bytes

data_uri = "data:image/png;base64," + encoded.decode("ascii")
print(data_uri[:40])

base64.b64encode() returns a bytes object, so decode it to a string before joining it with the prefix.

Build tools

Most bundlers and CSS pipelines can inline assets automatically: you import or reference the image as usual, and files below a configured size are replaced with data URIs at build time, while larger ones are emitted as files. The option names differ by tool, so check your bundler's asset documentation.

Decoding Base64 back to an image

To get the image file back, remove the data:...;base64, prefix and Base64-decode the rest into bytes. MDN stresses this for FileReader results: the string cannot be decoded as Base64 until the data URL declaration is removed.

# Linux
base64 -d logo.b64 > logo.png

# PowerShell
[IO.File]::WriteAllBytes("C:\img\logo.png", [Convert]::FromBase64String($b64))

# Python
import base64
with open("logo.png", "wb") as f:
    f.write(base64.b64decode(encoded))

If you do not know the format, look at the first characters: iVBORw0KGgo is PNG, /9j/ is JPEG, R0lGOD is GIF, UklGR is a RIFF container such as WebP, and PHN2Z is text starting with <svg.

Security and privacy

Base64 hides nothing and bypasses nothing by itself, but data URIs do interact with browser security rules. Four points matter.

  • Content-Security-Policy. If your site sends a CSP, data URI images load only when data: is listed, for example Content-Security-Policy: img-src 'self' data:. The CSP specification requires non-HTTP(S) schemes to be present explicitly; a wildcard does not cover them. Allow data: only in the directives that need it (usually img-src), not in script directives.
  • Top-level navigation is blocked. MDN states that top-level navigation to data: URLs is blocked in all modern browsers, because of phishing and related abuse. You cannot link a user to a data URI as a page; use it as a subresource, or use a Blob URL for downloads.
  • Base64 is not encryption. Anyone can decode it in one command. Do not use it to hide images, tokens or personal data, and remember that an image pasted into a JSON log or a URL is fully readable there.
  • Treat user-supplied SVG as code. SVG can contain scripts and external references. Browsers disable scripts when an SVG is used as an image, but the same markup is active if it is inserted into the page as inline SVG or opened as a document. Sanitize uploaded SVG on the server, or convert it to a raster format, before you display or re-serve it.

Limits, browser support and accessibility

Data URIs for images work in every current browser, and the practical limit is performance, not syntax. MDN says browsers are not required to support any particular maximum length; it lists 512 MB for Chromium and Firefox and 2,048 MB for Safari. Other software is stricter: RFC 2397 itself warns that applications may impose URL length limits, and text editors, proxies, databases and email systems often do.

Accessibility rules do not change. An inlined <img> still needs an alt attribute that describes it, or alt="" if it is purely decorative. A CSS background image has no text alternative, so never use one to carry information. Set width and height as you would for any image.

Common problems and fixes

Browsers give little feedback when a data URI is wrong; MDN notes that invalid parameters or a typo in base64 are ignored without an error. Most failures come down to one of these.

SymptomLikely causeFix
Broken image icon, string starts with iVBOR...Missing prefixAdd data:image/png;base64, before the data
Image does not render although the data is validWrong MIME type, such as image/jpg, or the default text typeUse the exact type from the table above
Image is cut off or the CSS rule is droppedLine breaks or spaces inside the stringEncode without wrapping (-w 0) and strip newlines
CSS background does not showUnquoted url() or clashing quotesWrap the URI in double quotes; use single quotes inside SVG
Decoded file is corruptDouble encoding, or the file was read as textEncode the raw bytes once; open files in binary mode
SVG data URI shows nothingUnencoded # or missing xmlnsWrite %23; keep the namespace attribute

Checklist

  • The image is small and needed immediately, or the document must be a single file.
  • The prefix has the right MIME type, ;base64 and a comma.
  • The string is one line, with no spaces or line breaks.
  • In CSS, the URI is inside quoted url("...").
  • SVG is URL-encoded instead of Base64 where possible, with # written as %23.
  • Your CSP allows data: in img-src, and nowhere it is not needed.
  • Every <img> still has appropriate alt text.

FAQ

How do I convert an image to Base64?

Read the file's bytes and Base64-encode them. On Linux run base64 -w 0 image.png; in PowerShell use [Convert]::ToBase64String([IO.File]::ReadAllBytes("image.png")); in a browser use FileReader.readAsDataURL(); in Python use base64.b64encode(). Then add the data:image/png;base64, prefix.

What is the difference between Base64 and a data URI?

Base64 is the encoding of the bytes. A data URI is a URL that wraps that encoded data with a scheme and a MIME type so a browser knows how to interpret it. An <img src> needs the full data URI; an API may want only the Base64 part.

How much bigger is a Base64 image?

About 33 percent: the length is 4 * ceil(n / 3) characters for n bytes, plus the prefix. A 3,000-byte file becomes 4,000 characters. HTTP compression of the surrounding HTML or CSS recovers much of that on the wire.

Is a Base64 image faster than a normal image?

Sometimes, for a very small image that is needed for the first paint, because it avoids a request. For larger or reused images it is usually slower overall: the page is heavier, the image cannot be cached separately and it cannot be lazy-loaded.

Should I Base64-encode SVG?

Usually not. URL-encode it and use data:image/svg+xml, without ;base64. It is shorter and stays readable. Base64 is still a safe fallback when a tool mangles special characters.

Can I use a Base64 image in CSS?

Yes. Put the data URI inside a quoted url("...") for properties such as background-image. The bytes then load with the stylesheet.

Why does my Base64 string end with = or ==?

That is padding. One = means the last group held 2 bytes; == means it held 1 byte.

Is Base64 a form of encryption?

No. It is a reversible encoding with no key, so it protects nothing.

Bottom line

A Base64 data URI is a simple trade: one fewer request in exchange for about a third more bytes and no separate caching. Use it for tiny, critical or self-contained images, URL-encode SVG instead of Base64-encoding it, and keep everything else as ordinary image files. When you do need the string, an image to Base64 converter, one shell command or a few lines of JavaScript or Python will produce it. You can find related utilities in our free developer tools.

Sources referenced in this guide

Previous Post Next Post