DEV Community

linkbruno.com
linkbruno.com

Posted on

How to find and fix broken images on a website

A broken image is easy to miss. On some pages it shows as a small icon with alt text, on others it leaves a blank gap, and if it's a CSS background you might see nothing at all. Visitors notice, though, and so do search engines when your product photos or diagrams return 404.

Here's why images break, how to find every broken one on a page or a site, and how to fix them so they stay fixed.

Why images break

Almost every broken image comes from one of these:

  • Case-sensitive paths. Photo.JPG and photo.jpg are the same file on macOS and Windows, but different files on a Linux server. An image that works locally can 404 in production.
  • Relative paths that moved. img/logo.png on /about means /about/img/logo.png, not /img/logo.png. Moving a page to a deeper URL silently breaks it.
  • Deleted uploads. Someone cleans up the media library or a CDN bucket, and old posts still reference the files.
  • Hotlinked images. You embedded an image from another site. They renamed it, removed it, or started blocking requests from other domains.
  • A migration. The domain or folder structure changed, and the image URLs inside old content didn't.
  • Only some sizes are broken. A srcset lists five widths and one of them is missing. Most desktop browsers look fine, but some phones get the broken one.

Find broken images on one page

DevTools Network tab

Open the page, press F12, go to Network, choose the Img filter and reload. Click the Status column header to sort. Anything 404, 403 or 5xx is a problem. This also catches CSS background images and images loaded by JavaScript, because the browser actually requested them.

Try a narrow window too, or switch on device emulation, so the browser requests the smaller srcset candidates.

A console snippet

For <img> tags, the browser already knows which ones failed. An image that finished loading with a width of zero didn't decode:

const broken = [...document.images].filter(
  (img) => img.complete && img.naturalWidth === 0
);
console.table(broken.map((img) => ({ src: img.currentSrc || img.src, alt: img.alt })));
Enter fullscreen mode Exit fullscreen mode

Scroll to the bottom of the page first. Lazy-loaded images (loading="lazy") aren't requested until they are near the viewport, so they won't show up as failed yet.

Check every srcset candidate

The browser only fetches one candidate per image. To test all of them, collect the URLs and request each one:

const urls = new Set();
document.querySelectorAll('img[src], img[srcset], source[srcset]').forEach((el) => {
  if (el.src) urls.add(el.src);
  (el.srcset || '').split(',').forEach((part) => {
    const u = part.trim().split(/\s+/)[0];
    if (u) urls.add(new URL(u, location.href).href);
  });
});

for (const url of urls) {
  const res = await fetch(url, { method: 'HEAD' }).catch(() => null);
  if (!res || !res.ok) console.warn(res ? res.status : 'failed', url);
}
Enter fullscreen mode Exit fullscreen mode

Cross-origin images may fail this check because of CORS even when they load fine. Verify those with curl.

Check images from the command line

curl is the most reliable way to see what the server actually returns:

curl -sI https://example.com/images/hero.webp | head -n 5
Enter fullscreen mode Exit fullscreen mode

Look at two things: the status line, and content-type. A 200 with content-type: text/html means the server returned an HTML page (often a "not found" template) instead of an image. That's a broken image that a status-only check would pass.

To test a list of image URLs at once:

while read -r url; do
  curl -s -o /dev/null -w '%{http_code} %{content_type}  '"$url"'\n' "$url"
done < images.txt | grep -v ' image/'
Enter fullscreen mode Exit fullscreen mode

Anything printed is either an error status or not an image.

Find broken images across a whole site

Checking pages one by one doesn't scale past a handful. For a full site, you need a crawler that reads every page and tests every image URL it finds. Desktop SEO crawlers can do this, and linkbruno.com checks images along with links in its free 25-page scan, then lists each missing image with the page it's on.

Whatever you use, make sure it reports the source page for each broken image. A list of dead image URLs is not much help if you can't find which posts contain them.

How to fix them

Wrong case or path. Rename the file or fix the reference so they match exactly. Prefer root-relative paths (/images/logo.png) or absolute URLs in content, so moving a page doesn't break its images.

Deleted files. Restore from a backup or re-upload. If you can't, replace the image or remove the <img> tag. Don't leave an empty frame on the page.

Hotlinked images. Download the image (if you have the right to use it) and host it yourself. Relying on someone else's server for your page content is how this happened in the first place.

After a migration. Add redirects from the old image URLs to the new ones, the same way you would for pages, and then update the content so the redirect isn't needed.

Missing srcset sizes. Regenerate the image sizes. In WordPress, a thumbnail regeneration plugin does this; in a static site, rerun the image build step.

Make broken images less visible when they do happen

Images will break again eventually, so give them a graceful failure:

<img src="/images/chart.png" alt="Monthly signups, Jan to Jun" width="800" height="450">
Enter fullscreen mode Exit fullscreen mode
  • Meaningful alt text shows in place of the image and keeps the content understandable.
  • width and height reserve the space, so a missing image doesn't make the layout jump.

You can also hide the broken-image icon with a small handler, but use it as a safety net, not a fix:

document.addEventListener('error', (e) => {
  if (e.target.tagName === 'IMG') e.target.classList.add('img-missing');
}, true);
Enter fullscreen mode Exit fullscreen mode

A short checklist

  1. Run the console snippet on your key pages after every deploy.
  2. Keep image paths lowercase and root-relative.
  3. Host your own images instead of hotlinking.
  4. Ship redirects for old image URLs when you migrate.
  5. Crawl the whole site for images, not just links, a few times a year.

Most broken images on a typical site trace back to one or two causes. Once you find the pattern, the fix is usually a single rename or redirect rule.

Top comments (0)