# Images Not Showing in an HTML File You Sent or Opened Elsewhere — the Fix

Canonical: https://commareports.com/fix/html-images-not-showing
Published: 2026-10-03

> The page has broken-image icons everywhere except on your own machine. The six path mistakes behind it — relative files left behind, C:\ paths, root-relative URLs, case-sensitive servers, expired signed URLs, http on https — and how to make the images travel with the file.

# Images not showing in an HTML file

On your machine the report is fine. Sent to someone else — attached to
an email, dropped in Slack, uploaded to a server — every chart is a
broken-image icon and the alt text is showing where the pictures were.

The HTML is intact. The images just never came with it.

## Find the cause in 30 seconds

Open the page where it is broken, right-click a broken image and choose
**Open image in new tab** (or check DevTools → **Network**, filter
**Img**). The URL it tried tells you which of these it is:

| The failed URL looks like                  | Cause                                   |
| ------------------------------------------ | --------------------------------------- |
| `file:///…/Downloads/img/chart.png`        | Relative file that was never sent       |
| `file:///C:/Users/you/project/…`           | Absolute path to **your** disk          |
| `file:///images/logo.png`                  | Root-relative path opened from disk     |
| `https://site/img/Chart.PNG` → 404         | Case mismatch on a Linux server         |
| `https://bucket.s3…?X-Amz-Expires=…` → 403 | Signed URL that has expired             |
| `http://…` on an `https://` page, blocked  | Mixed content                           |
| `http://localhost:…`                       | Only exists on the machine that made it |

## 1. The images are separate files, and only the HTML travelled

By far the most common. Notebook exports, test reporters, coverage
tools and coding agents write the page **and** a folder of images:

```
report/
  index.html        ← you sent this
  img/
    latency.png     ← not this
    errors.png      ← or this
```

`<img src="img/latency.png">` is a pointer to a neighbour. The
recipient's copy has no neighbours.

**Fixes, from quickest to sturdiest:**

- **Embed the images in the file.** Drop the whole folder into the
  [HTML inliner](/tools/html-inliner) and download one `.html` with every
  image, stylesheet and font inside it. For a single picture, the
  [image to Base64 converter](/tools/image-to-base64) gives you the
  `data:` URI to paste in.
- **Send the folder as a zip.** Works, but the recipient has to unzip
  it, keep the structure intact, and open the right file — and mail
  filters often strip zips containing `.html`.
- **Send a link instead of a file.** [Publish the
  folder](/share-html-folder) and every relative path keeps resolving,
  because the assets are hosted next to the page.

## 2. An absolute path to your own disk

```html
<img src="C:\Users\dana\project\out\chart.png" />
<img src="file:///Users/dana/project/out/chart.png" />
```

Some tools and copy-pastes write the full local path. It works for
exactly one person. Replace it with a path relative to the HTML file
(`out/chart.png`), then apply fix #1. Backslashes are a Windows-ism —
use forward slashes in `src`, even on Windows.

## 3. A root-relative path, opened from disk

```html
<img src="/static/logo.png" />
```

The leading `/` means _the root of the site_. Behind a web server that
works. Double-clicked from a folder, the page is on `file://`, and the
root is your drive — so the browser looks for `/static/logo.png` on
the filesystem. Drop the leading slash, or open the folder through a
[local server](/serve-html-file-locally) rather than by double-clicking.

## 4. Letter case on a Linux server

`Chart.PNG` and `chart.png` are the same file on Windows and on macOS's
default filesystem. On Linux, on S3 and on most static hosts they are
two different names, and only one exists. Everything works locally,
then 404s on deploy. Rename the files or the `src` so the case matches
exactly — extension included.

## 5. An expired or private URL

The image is hosted, but behind a pre-signed link (`X-Amz-Expires`,
`se=` on Azure, `Expires=` on GCS) that was valid for an hour when the
report was generated. A week later it returns 403. The same happens
with images in a private bucket, a logged-in dashboard, or a Google
Drive "anyone with the link" file that isn't actually an image URL.

Either embed the image so it never depends on that URL again, or host
it somewhere public and stable.

## 6. `http://` images on an `https://` page

Browsers upgrade or block plain-HTTP images on secure pages — see
[mixed content blocked](/fix/mixed-content-blocked-html-report). And
anything on `localhost` was only ever reachable from the machine that
generated it; see [the localhost link doesn't work for
others](/fix/localhost-link-doesnt-work-for-others).

## Embed or host?

|                        | Embed (data URI)          | Host next to the page        |
| ---------------------- | ------------------------- | ---------------------------- |
| Works as an attachment | Yes                       | No — needs a link            |
| File size              | ~33% bigger per image     | Unchanged                    |
| Caching                | Re-downloaded every visit | Each image cached separately |
| Good for               | Charts, icons, a logo     | Photos, many large images    |

For a report with a handful of charts, embedding is the right call — the
file stops being fragile. Past a few megabytes of images, a link is
kinder to everyone's inbox; see [HTML file too big to
email](/fix/html-file-too-big-to-email).

## Or send a link, not a file

[Publish the report folder to Comma](https://commareports.com/new) and
send one URL. The images, stylesheets and scripts are uploaded alongside
the page, so nothing goes missing; readers comment on the exact chart
they mean; and a re-run replaces it as a new revision at the same link.
Lock it to a password, an invite list or your email domain when it
isn't public.

## Try it

**[Inline a page into one file →](/tools/html-inliner)**

### Related

- [HTML inliner](/tools/html-inliner) — pack a page and its CSS, JS, images and fonts into one file
- [Image to Base64](/tools/image-to-base64) — one image to a data URI
- [HTML report lost its CSS](/html-report-broken-css)
- [Share an HTML folder](/share-html-folder)
- [The HTML file is too big to email](/fix/html-file-too-big-to-email)
- [All report troubleshooting](/fix)
