Plugin
Freshet Unused Media Pro
The gripe
I can't reliably clean up the media library, because WordPress cannot tell me whether a file is in use.
The plugin
Should look everywhere a reference can hide, and show me the evidence it finds, instead of guessing.
The Uploaded to column records where a file was first attached and nothing more. So an image used in an ACF field, a page-builder shortcode, a customizer setting or a plain URL in content looks exactly like one nothing has pointed at in years.
Every media cleaner has to answer the same question: is this file safe to delete? This one answers it by looking everywhere a reference can hide, showing its evidence, and refusing to guess.
Free, and the whole plugin
Scanning, detection and deletion are free and stay free. Nothing in the download is locked, limited or time-barred.
It finds references nothing else looks for
-scaled files.
Read the technicalities in the docs.
It is built to be wrong in one direction only
wp-image-1234.
Every file is re-checked in the instant before it is deleted
The evidence is shown, not summarised
The blind spot is written down
freshet_unusedmedia_detectors lets you add a detector of your
own that queries your table and reports back like any built-in one.
The hook reference is in the docs.
Measured on a real library, not a demo site
A real agency-managed WordPress library: 38,797 attachments accumulated over eight years, ten gigabytes of uploads, 3,656 distinct files on disk (the site is multilingual, so one upload carries about ten library rows). WordPress' own Unattached filter marks 23,659 of those rows — three in five — as apparently free to delete.
The scan says otherwise. On a random sample of 450 files, about one in eight of the library, 438 turned out to be referenced somewhere WordPress does not look — page content, featured images, block fields, theme options — and 12 came back with no reference at all. Of the Unattached rows in that sample, 98% sat on a file the site still uses. Deleting on WordPress' own signal would have taken live files with it nearly every time; the scan narrows the job to the 3% that can go.
Looking that carefully has a cost: about 14 seconds per file on a laptop, so a first full pass of a library this size is an overnight job — around fourteen hours. Later scans are quick. Nothing was deleted while measuring: the scan ran on an offline copy, scan only. The full write-up is on the blog.
Needs WordPress 6.5 and PHP 8.1 or newer. Download it from wordpress.org, or read the source on GitHub. Or Try it in WordPress Playground first: a throwaway site in your browser, the plugin active and a media library already seeded to scan.
What Pro gets you
Four additions. None of them deletes anything.
The Used view
See every place a file is used. Useful where you want to replace rather than remove. You get the full list of what would break, before you touch anything, and something to hand over in case it needs to be signed-off.
WP-CLI
wp freshet-unusedmedia scan and list — the same
scan across many sites without a browser, read back as JSON.
Evidence report
Export the library as CSV or JSON: one row per file, with its status, its size and where it was found.
Space totals
How much disk the unused files are holding now, and separately, how much has actually been freed by the deletions made here.