The introductory post covered what Media Usage does. This one is about how it does it — the scanning model, where it looks, and the limits it imposes on itself to stay safe on a live site.
Source-first, not attachment-first
The obvious way to build a “where is this image used” index is to loop over every attachment and search the site for it. Media Usage does the opposite: it loops over every source — posts, pages, custom post types, templates, relevant metadata, and a bounded set of site options — once each, and extracts every media-shaped reference it finds along the way. Each source is parsed a single time rather than re-parsed once per attachment, which is what keeps the scan’s cost proportional to content size instead of content size multiplied by media count.
A reference can be an attachment ID, a generated markup class, an original URL, a resized-file URL, or a CDN-relative path. Attachment-ID resolution handles the cases where the same image is referenced three different ways across a site and should collapse to one evidence record, not three.
Builder coverage without claiming the earth
Most of what makes an image hard to find isn’t core WordPress — it’s builder data. Media Usage recognizes media-shaped structured fields rather than running generic text search, with provider-specific extraction for:
- Core blocks via
parse_blocks(), classic HTML, galleries, and featured images. - Elementor’s
_elementor_dataand_elementor_page_settings. - WPBakery
[vc_*]and Uncode[uncode_*]shortcode attributes. - Divi
[et_pb_*]shortcodes and_et_*structured post metadata. - ACF image, gallery, and file fields — including repeaters and Options Page storage when field definitions are available.
The label on each of these is deliberately “detected evidence,” not “full support for this builder.” A provider tag means the scanner recognizes that builder’s known storage patterns, not that every widget or theme-option screen has been audited. Reusable templates and library content get a conservative “Referenced” status rather than being scored as unused, because proving a template isn’t assigned anywhere is much harder than proving a page uses an image directly.
Why “No references found” and not “Unused”
This is the one design decision that shapes everything else. A local database scanner cannot see custom PHP, external systems, conditional JavaScript, or inaccessible pages. So instead of a single confident “unused” verdict, every image gets one of six statuses: In use, Referenced, Needs review, No references found, Protected, or Not scanned.
“Needs review” exists specifically for the case where a source couldn’t be scanned completely — a 2 MB page, say — and zero matches isn’t yet a safe conclusion. The dashboard names the skipped source, its type, the reason, and the limit it hit, rather than silently dropping it from the count.
Resumable, incremental, and bounded on purpose
A complete scan runs in phases — relationship cleanup, settings, content, image finalization — each independently resumable. Workers yield after roughly 2.5 seconds or at 80% of the PHP memory limit, and batches are capped (10 site settings, 25 content sources, or 100 images per pass, at least 2.5 seconds apart). None of this is tunable in the UI; it’s a fixed safety margin so a scan can’t stall a shared host, whatever the site size.
After the first complete scan, Media Usage doesn’t start over from scratch when content changes. Saving a post, changing post metadata, editing a watched site option, or uploading/regenerating an image queues a targeted incremental rescan of just that source. A full reconciliation re-runs 30 days after each successful scan, as a standing correction for anything an incremental pass could have missed. Incrementals are skipped automatically during WP_IMPORTING and ignore noisy meta like _edit_lock.
Results are published generation-by-generation: a rebuild writes to a new generation while every read continues against the last successful one, and a new generation is only promoted after the rebuild actually completes. Nothing reading the dashboard mid-rescan sees a half-built index.
What Pro adds — and only that
Media Usage Pro sits entirely on top of this index through a versioned public API, and it adds one feature: a reversible Cleanup Queue. An image becomes eligible only when the base plugin’s latest index is warning-free, the attachment is an image, its status is no_references, its reference count is zero, and it isn’t manually protected. Queuing hides the image from the Media Library; it never moves the file, changes attachment status, or rewrites a URL. Deactivate Pro and every queued image just reappears — no license check gates restoration, ever.
Nothing here deletes anything. That’s intentional, and it’s the subject of its own future post once permanent deletion actually ships with the safeguards it needs.