Why does an image on my website show a broken icon?
A broken image icon means the browser requested the image and could not use the response: a missing file (404), a refused request (403), a blocked insecure address, or a web page returned instead of an image. In developer tools, filter the Network panel to images and read the status and content type of each failing request.
The short version
- The HTML Standard says the status code is ignored when deciding whether an image is valid: what counts is whether the bytes are a supported image. So a 404 that carries real image bytes can still display, and a 200 that carries HTML does not.
- The icon itself is up to the browser: the standard says it is shown “optionally”. If the image has non-empty
alttext, that text is shown instead of the picture. - An
alt=""image that fails to load shows nothing: visual browsers hide the broken icon whenaltis empty. - A CSS
background-imagethat fails to load shows no icon and no alt text; the browser treats it asnone. - Browsers upgrade an
http://image on an https page, and block some cases (see the table).
Causes, and how each shows up
| Cause | What you see in the Network panel | Notes from the official pages |
|---|---|---|
| The file is missing or moved | Status 404 | MDN: common causes of a 404 are mistyped URLs or files “moved or deleted without redirection”. |
| The URL returns a page, not an image | Status 200, but the content type is text/html instead of an image type | The HTML Standard: image data that is not in a supported format sets the request to broken. The status is ignored. |
| The path differs in letter case | Status 404 for /Img/Photo.PNG when the file is photo.png | RFC 3986: apart from scheme and host, URL parts are “assumed to be case-sensitive”. Whether a server maps a path to a file case-sensitively depends on the server. |
| An insecure image on an https page | A console message about mixed content | MDN: browsers auto-upgrade img from http to https, “but not when setting resources with srcset or <picture>”, and block it if the host is an IP address. If the https address doesn't serve the file, it fails. |
| Another site's rules refuse the request (hotlink protection) | Status 403 on an image from another site | Cloudflare's hotlink protection “denies access to requests when the HTTP referer does not include your website domain name (and is not blank)”. The page does not say what the visitor sees. |
| A Content Security Policy or cross-origin policy blocks it | The request is marked blocked, with a console message | MDN: img-src limits valid image sources; CORS and Cross-Origin-Resource-Policy can block cross-origin images. |
A srcset that lists no usable file | No image request at all | MDN: with w descriptors, src is not considered. Google recommends always giving a fallback src. |
An image that has not loaded yet is not necessarily broken: MDN says lazy-loaded images are not loaded until they intersect a visible part of the page.
Find it in your browser
- Open developer tools, choose the Network panel and reload the page.
- Filter to images (Chrome has an Img button) and look for rows with a red status or an odd status.
- Read the Status and the Type columns. Chrome's reference describes Type as “The MIME type of the requested resource”. A 200 whose type is
text/htmlis a web page served in place of an image. - Check the Console for mixed-content and CORS messages.
Find them for a whole page
Paste the page address. The checker lists every image the HTML refers to and reports the ones that fail to load, redirect, or load over http://.
What we tested
On 2026-10-13 we built a test page with seven <img> elements and ran the live Broken Link Checker on it. The test site is a preview that search engines are told not to index and is not part of this site.
| The image | What the checker reported |
|---|---|
| A working PNG | Counted as answered normally. |
| A file that does not exist | “Broken image (404)”: Fix before publishing. |
| The same file name in a different letter case (on this host, a 404) | “Broken image (404)”: Fix before publishing. |
| An image on another site that answers 404 | “Broken image (404)”: Fix before publishing. |
| An image on another site that answers 403 | “Image answered 403 to our checker”: Review. “It may work for visitors — open it in a browser to confirm.” |
The same working PNG requested as http:// on an https page | “Image loaded over http:// on an https page”: Review. |
A web page (status 200, text/html) used as the image | Not reported. The summary read “2 of 7 links and resources answered normally”: the working PNG and this one. |
So the checker catches missing files, refused requests and insecure images, but it judges an image by its status code, not by what the response contains. A 200 that is really HTML slips through; the Network panel's Type column catches it. For og:image the Open Graph Checker does check the content type: it reported “og:image is not an image (text/html; charset=utf-8)” in the same situation.
What this can't tell you
It reads the HTML, so images added by JavaScript and CSS backgrounds are not part of the check. It fetches from our server: a request that is refused for us may work for a visitor, and the reverse. It does not say why a request failed, only what came back.
Related: Mixed content and Open Graph image not showing.
Sources
We read these official pages on 2026-10-13.
- HTML Standard: Images (updating the image data) (page dated 2026-10-13)
- HTML Standard: The img element (page dated 2026-10-13)
- <img>: The Image Embed element - MDN
- Mixed content - MDN
- RFC 3986: Uniform Resource Identifier (URI): Generic Syntax
- Hotlink Protection - Cloudflare WAF docs
- background-image - MDN
- Network features reference - Chrome DevTools (page dated 2024-07-16)
- Image SEO Best Practices (page dated 2026-03-02)
Fixed this one? Before you send people to the page, run the full pre-publish check for everything else: indexing signals, link preview, broken links, alt text, structured data and HTTPS in one pass.