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.
In this guide
- What Base64 is and how it works
- What a data URI is
- How to use a Base64 image
- The size and performance cost
- When inlining helps and when it hurts
- The SVG special case
- How to convert an image to Base64
- Decoding Base64 back to an image
- Security and privacy
- Limits, browser support and accessibility
- Common problems and fixes
- Checklist
- FAQ
- Bottom line
- Sources
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 asimage/png. If you leave it out, it defaults totext/plain;charset=US-ASCII, which is wrong for an image.;base64says 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
| Format | MIME type | Data URI prefix |
|---|---|---|
| PNG | image/png | data:image/png;base64, |
| JPEG | image/jpeg | data:image/jpeg;base64, |
| GIF | image/gif | data:image/gif;base64, |
| WebP | image/webp | data:image/webp;base64, |
| SVG | image/svg+xml | data:image/svg+xml;base64, or data:image/svg+xml, |
| ICO | image/vnd.microsoft.icon (official); image/x-icon is also widely used | data: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 size | Base64 length | Increase |
|---|---|---|
| 1 byte | 4 characters | 300% |
| 100 bytes | 136 characters | 36.00% |
| 1,000 bytes | 1,336 characters | 33.60% |
| 3,000 bytes | 4,000 characters | 33.33% |
| 10,240 bytes (10 KiB) | 13,656 characters | 33.36% |
| 1,048,576 bytes (1 MiB) | 1,398,104 characters | 33.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 URI | Poor candidates |
|---|---|
| Tiny icons and UI glyphs used on one page | Photos and other large images |
| A small blurred or solid placeholder shown before the real image loads | Logos and icons reused across many pages |
| Single-file documents: reports, demos, offline HTML, email-like exports | Images below the fold that could be lazy-loaded |
| Environments where a second request is impossible or awkward | Images 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:
- Use single quotes inside the SVG so they do not clash with the double quotes around the CSS URL.
- Encode
<as%3Cand>as%3E. - Encode
#as%23. An unencoded#starts a URL fragment and cuts the image off. This is the most common mistake with hex colors. - Encode any literal
%as%25. - Keep the
xmlnsattribute. 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 exampleContent-Security-Policy: img-src 'self' data:. The CSP specification requires non-HTTP(S) schemes to be present explicitly; a wildcard does not cover them. Allowdata:only in the directives that need it (usuallyimg-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 aBlobURL 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.
| Symptom | Likely cause | Fix |
|---|---|---|
Broken image icon, string starts with iVBOR... | Missing prefix | Add data:image/png;base64, before the data |
| Image does not render although the data is valid | Wrong MIME type, such as image/jpg, or the default text type | Use the exact type from the table above |
| Image is cut off or the CSS rule is dropped | Line breaks or spaces inside the string | Encode without wrapping (-w 0) and strip newlines |
| CSS background does not show | Unquoted url() or clashing quotes | Wrap the URI in double quotes; use single quotes inside SVG |
| Decoded file is corrupt | Double encoding, or the file was read as text | Encode the raw bytes once; open files in binary mode |
| SVG data URI shows nothing | Unencoded # or missing xmlns | Write %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,
;base64and 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:inimg-src, and nowhere it is not needed. - Every
<img>still has appropriatealttext.
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
- IETF: RFC 4648, The Base16, Base32, and Base64 Data Encodings
- IETF: RFC 2397, The "data" URL scheme
- MDN: data: URLs
- MDN: Base64 (glossary)
- MDN: Image file type and format guide
- MDN: FileReader.readAsDataURL()
- MDN: HTMLCanvasElement.toDataURL()
- MDN: Window.btoa()
- MDN: Uint8Array.prototype.toBase64()
- MDN: CSS url() function
- MDN: SVG as an image
- MDN: The img element
- MDN: HTML rel attribute (icon)
- MDN: Compression in HTTP
- MDN: HTTP/2 (glossary)
- MDN: HTTP/3 (glossary)
- MDN: Content-Security-Policy: img-src
- W3C: Content Security Policy Level 3
- web.dev: Don't fight the browser preload scanner
- GNU Coreutils manual: base64 invocation
- Microsoft Learn: certutil
- Microsoft Learn: Convert.ToBase64String
- Python documentation: base64