How to embed an image in HTML with base64

Replace the src path with a data URI and the picture lives inside the HTML. The file travels alone, at the cost of roughly a third more bytes.

To embed an image in HTML as base64, put a data URI in the src where the file path used to be:

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..." alt="Company logo" width="180">

The picture is now part of the HTML. Send the file anywhere and the image goes with it, because there is nothing separate left to lose.

The same page opened from a different folder. The base64 logo renders; a path-based image beside it does not.
The same page opened from a different folder. The base64 logo renders; a path-based image beside it does not.

The three parts of the URI

data:image/png;base64,iVBOR... breaks into pieces that each have to be right.

  • data: says the content follows inline rather than at an address.
  • image/png is the MIME type. Use image/jpeg, image/gif, image/webp or image/svg+xml as appropriate.
  • ;base64 says the payload is base64 encoded.
  • Everything after the comma is the payload, on one unbroken line.

Get the type wrong and browsers usually still render it, but not always, and a mismatch is the sort of thing that works locally and fails in one place. Data URI covers the format beyond images.

Encoding a file

macOS or Linux:

base64 -i logo.png | tr -d '\n' > logo.txt

Windows PowerShell:

[Convert]::ToBase64String([IO.File]::ReadAllBytes("logo.png")) > logo.txt

Strip the line breaks. Some tools wrap the output at 76 characters, and a wrapped string inside an attribute will not decode.

Paste the result after the comma. It will be long. A small logo runs to thousands of characters, and that is normal.

The size cost

Base64 stores three bytes in four characters, so the text is about 33 percent larger than the original file.

Original As base64 Sensible inline
2 KB icon ~2.7 KB Yes
20 KB logo ~27 KB Yes
200 KB photo ~267 KB Only if the file must travel alone
2 MB screenshot ~2.7 MB No, host it

There is a second cost beyond bytes. A separate image file is cached by the browser and reused across pages; an inline one is re-downloaded with every copy of the HTML.

For one document sent once, that does not matter. For a site with a repeated header logo, it does.

Compress before you encode. Resizing a 2000 pixel screenshot down to the 800 pixels it is displayed at often saves more than the encoding costs.

A file listing showing the original image and the HTML file after the image was inlined.
A file listing showing the original image and the HTML file after the image was inlined.

SVG does not need base64

SVG is text already, so you can inline it directly and skip the size penalty:

<img src="data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'>...</svg>">

Use single quotes inside and percent-encode any # as %23, since a hash starts a fragment. Better still, paste the <svg> element straight into the page, where CSS can reach its parts. SVG in HTML goes through both routes.

In CSS as well

The same URI works anywhere CSS takes a URL:

.hero { background-image: url("data:image/jpeg;base64,/9j/4AAQSkZJRg..."); }

This is how a self-contained page keeps its background images. The stylesheet has to be inline too, or the CSS file becomes the thing that goes missing.

When to embed an image as base64, and when not to

Situation Inline as base64
Small icon or logo in a page you will send as a file Yes
A page that must work with no network Yes
Photo gallery No, host the images
Site header repeated across many pages No, it defeats caching
An email body No, several clients block data URIs
A page you are sending as a link Not needed, reference the address

That last row matters. Inlining is a workaround for files that travel on their own. Once the page has an address, the images can have addresses too.

Where it still breaks

Content Security Policy. A strict img-src rule blocks data URIs unless data: is listed. See content security policy.

Mail clients. Several strip or block inline image data, leaving an empty box.

Editing. A thousand-character attribute in the middle of your markup makes the file hard to read and diff. Keep inlining as a final step rather than an authoring habit.

A page where a strict image policy blocked a data URI. The alt text shows in place of the picture.
A page where a strict image policy blocked a data URI. The alt text shows in place of the picture.

The alternative to all of this

Inlining exists because a file sent on its own arrives without its neighbours. Give the page an address and that problem disappears.

Paste the HTML into a NOS document and it renders as written, with inline images or hosted ones. The document has its own link, so nothing has to be folded in to survive the trip.

Self-contained HTML covers the whole one-file approach, images not showing in HTML covers the failure it is meant to prevent, and turning HTML into a link is the route that avoids needing either.

Checking that it worked

Inlining is easy to get almost right, and the failures are quiet. Three checks settle it.

Move the file. Copy the HTML to a folder with no images in it and open it there. Anything that still draws is genuinely inside the file.

Look for the alt text. A broken data URI usually shows the alt text rather than an error, so an image that silently became a line of words is the sign of a truncated string.

Check the start of the payload. A PNG payload begins iVBOR, a JPEG begins /9j/, and a GIF begins R0lGOD. If the declared type and that prefix disagree, the type is wrong.

The page opened from an empty folder. The inlined image renders with no other files present.
The page opened from an empty folder. The inlined image renders with no other files present.

One habit saves trouble later. Keep the original image file next to the source, even after inlining, because you cannot usefully edit a picture that exists only as an attribute.

Questions people ask

What does a base64 image in HTML look like?

The src holds a data URI instead of a path: data:image/png;base64, followed by the encoded bytes. The browser decodes it and draws the picture. No separate file is fetched, so nothing can go missing.

How do I convert an image to base64?

On macOS or Linux, run base64 -i logo.png. On Windows, use the ToBase64String method in PowerShell on the file bytes. Both produce one long line you paste after the comma in the data URI.

Does base64 make the file bigger?

Yes, by roughly a third. Base64 represents three bytes with four characters. A 90 KB photo becomes around 120 KB of text, and that text sits in the HTML rather than in a cacheable separate file.

Will a base64 image show up in email?

Often not. Several mail clients block data URIs in images as a security measure, showing an empty box. For email, host the image at an address and reference it, or send a link to the page instead.

Keep reading