Skip to main content

Find & fix broken product images

Over time, product images rot: a local file gets pruned, a supplier takes down a hotlinked URL, a CDN path changes. A broken primary image is the one that hurts — it shows as a placeholder on the product, on packing slips, and anywhere the catalog is surfaced. SKU.io ships a two-part tool to find and clean these up: an on-demand image health check that re-validates every image, and a Broken Product Images report that lists the confirmed-broken ones so you can delete the dead images or clear false positives.

The design is deliberately cautious. The health check only flags an image broken when it's definitively gone; anything ambiguous (a host blocking automated traffic, a timeout, a server hiccup) is left alone. The report then loads each flagged image live in your browser as the final tie-breaker — images that actually render clear themselves automatically.

Before you begin

  • You need read/write access to Products to run the health check and to delete or clear images. See the product permissions reference.
  • The health check runs as a background job. You watch its progress in the job tray (the progress tray at the bottom of the screen) and can cancel it there.
  • For adding, reordering, and setting a primary image on an individual product, see Add & manage product images.

Run the image health check

The health check re-validates every product image in your catalog and records the result on each image. Run it before working the report so the report reflects current reality.

You can start it from either place:

  1. Open the Product Data Health dashboard and, in the Image Health card, click Check Image Health. Or, from the report itself, click Re-check All Images.
  2. The check dispatches as a background job named Check Image Health. Watch it in the job tray.
  3. As it runs, the job reports live counts:
    • images checked — total images processed so far
    • images resolvable — confirmed OK this run
    • images broken — confirmed definitively gone
    • products with broken primary image — products whose primary image came back broken
  4. When it finishes, the job summary reads Checked N images — M broken. The dashboard's broken-image count and last-checked timestamp refresh automatically, and the report reloads.

Notes and edge cases:

  • It's cancellable. Cancel from the job tray and the sweep stops at the current chunk; images already checked keep their fresh result.
  • Large remote catalogs are slow. Remote images are validated over HTTP one at a time with deliberate pacing, so the job has a 30-minute timeout and doesn't retry (tries = 1). If it's cancelled or times out, simply run it again.
  • It has no side effects on your products. The sweep writes each result directly to the image row and never triggers the usual image-save behavior — it won't reset which image is primary and won't re-queue packing-slip prints.

Work the Broken Product Images report

The report lists only images the health check confirmed broken — those with a check timestamp and a broken flag. Open it from the Products with Broken Image tile on the Product Data Health dashboard (the tile is clickable once at least one health check has run), or go directly to Reports → Broken Product Images.

The report is paginated (25 per page by default; you can switch to 10 or 50). Each row shows:

ColumnWhat it shows
ImageA live thumbnail — the browser attempts to load the real image URL
Browser CheckChecking…, then Loads — cleared (self-healed) or Failed to load
ProductThe product SKU (links to the product detail page) and name
Image URLThe image's URL, with copy-to-clipboard and open-in-new-tab buttons
PrimaryWhether this is the product's primary image
Last CheckedWhen the health check last validated this image

The browser load is the final tie-breaker

The server's probe is conservative and can produce false positives — a host that blocks automated requests may render fine in a real browser. So the report re-tests every flagged image the honest way: it loads the actual image in your browser.

  • A thumbnail that loads proves the image still works. That row is marked OK automatically and drops off the list — no action needed. A running tally of these appears as an auto-cleared chip.
  • A thumbnail that fails to load is genuinely broken and shows a broken-image icon. These are the rows worth cleaning up.

Because the report clears false positives as you view them, the on-screen broken count ticks down while you work.

Bulk-delete confirmed-broken images

Once the browser check has separated the genuinely dead rows from the false positives, remove the dead ones:

  1. Select the failed rows. Use the row checkboxes, or click Select all failed to select every row whose thumbnail failed to load on the current page.
  2. Click Delete Selected.
  3. Confirm in the dialog.

You can also delete a single row with its row-level delete button.

What deletion does:

  • Removes the image from its product. Deletion goes through the full image-delete path: the server-stored file (for local images) is removed, and the parent product's primary image is reset if you deleted its primary.
  • Re-queues packing slips. If a deleted image affected a product's primary image, affected packing slips are re-queued so reprints use the corrected image.
  • Limit: 500 images per action. If you need to remove more, delete in batches. Every selected image must still exist — if one was already removed, the action reports that one or more selected images no longer exist.

Mark false positives as OK

If a row is a false positive that the automatic browser check didn't clear on its own — for example, you confirmed the image loads by opening its URL in a new tab — you can clear it manually:

  1. Select the rows.
  2. Click Mark as OK.

This clears the broken flag so the image no longer counts against the product and drops off the report. Edge cases:

  • It's re-verified server-side first. Marking OK doesn't blindly trust the request. The server re-probes each selected image and refuses to heal any that's definitively gone — an empty URL, a missing local file, or a remote URL returning HTTP 404/410. Everything else (blocks, throttles, timeouts, 5xx) is treated as ambiguous and does heal, because a browser routinely loads images a server-side probe can't. Only the images that pass re-verification are cleared, so the "marked" count can be lower than the number you selected.
  • Limit: 500 images per action.

How images are classified: Broken vs OK vs unknown

Understanding the three outcomes explains why a URL you think is dead sometimes isn't flagged, and why the report re-checks in your browser.

The health check validates each image against its storage type:

  • Local images (a file on your tenant's image storage) are checked authoritatively and cheaply — the file either exists on disk or it doesn't.
  • Remote images (a full external URL) are probed over HTTP: a HEAD request first, falling back to a ranged GET that pulls just the first bytes to sniff the file's magic number when the response type is inconclusive. The probe sends a real browser-like Accept header and a same-origin Referer, and adds a little random delay between requests, so it slips past the common hotlink and bot protections that would otherwise reject it.

The outcome is one of three:

OutcomeMeaningWhen it happens
OK (resolvable)The image is confirmed presentLocal file exists; remote URL returns an image (confirmed by content type or magic bytes)
BrokenThe image is definitively goneEmpty URL, missing local file, or remote HTTP 404 / 410
Unknown (unchanged)Can't tell — left as-isHTTP 401, 403, 405, 429, any 5xx, timeouts, DNS/connection errors — that is, access, throttling, or availability problems, not a missing image

Broken is reserved for a definitive "gone." A 403 Forbidden or a timeout is not treated as broken, because hostile hotlink/WAF/bot protection routinely blocks automated requests for images that load perfectly in a browser. Those stay unknown and their flag is left untouched — only the check timestamp is updated. This is why the report's live browser load is the authoritative tie-breaker for anything the server couldn't confirm.

The per-host circuit breaker

If a single remote host returns a run of ambiguous failures (8 in a row), the check stops probing that host for the rest of the run and treats its remaining images as unknown. A whole host going dark mid-sweep is that host defending itself against bulk automated traffic — not thousands of its images dying at once. The circuit breaker is what prevents one hostile host from mass-flagging its entire catalog as broken.

Why the flag needs the health check: resolvable & image_checked_at

Two fields on each image drive all of this, and knowing how they behave explains why running the check matters:

  • resolvable — the broken/OK flag. It defaults to OK on a new image and is never re-evaluated by an upload alone. Uploading or editing an image doesn't, by itself, confirm the URL still works.
  • image_checked_at — the timestamp of the last health check on that image.

A product is counted as having a broken primary image only when all three hold: the image is the product's primary, its flag isn't OK, and it has been checked at least once (image_checked_at is set). Requiring the timestamp is what stops never-checked images from being counted as broken purely because of a default.

Because the flag is never touched on upload, it drifts out of date in two ways — and the health check is what corrects both:

  • Dead-URL-still-OK — an image whose URL died after upload but still reads OK. The check re-probes it and flips it to broken.
  • Fixed-URL-stuck-broken — an image that was broken, then fixed, but stayed flagged broken. The check re-probes it, confirms it loads, and clears the flag.

Each run rewrites resolvable with the fresh result and stamps image_checked_at, keeping the report and the dashboard's broken-image count honest. Run the check on a schedule (or before a big catalog review) rather than assuming a one-time sweep stays accurate.

Next steps

Video transcript

The Broken Product Images report lists every image the health check flagged as unresolvable. Re-check All Images re-runs the check. Each thumbnail loads live in your browser — one that appears still works and clears itself; one that fails is genuinely broken. Here CD-WMS-1L's image failed to load — the URL is dead. The row shows the product, the image URL, and when it was last checked. Select the broken rows and Delete Selected to remove the dead images from their products.

Last verified: