Over time, your WordPress site collects hundreds — or thousands — of images, many of which are no longer used in posts, pages, or even the theme. These unused media files clutter your server and can slow down backups or migrations.
In this tutorial, you’ll learn how to scan your WordPress Media Library for images that aren’t currently used anywhere — and optionally delete them. This method avoids heavy plugins and gives you control with custom PHP.
⚠️ Important: Always back up your site before deleting any files.
🎯 What You’ll Build
A script that:
Loops through all media attachments
Checks whether they’re used in posts or pages
Flags unused files
Optionally deletes them
✅ Step 1: Create a List of Used Image URLs
We’ll search through post content for URLs pointing to image files.
function get_used_image_urls() {
global $wpdb;
$results = $wpdb->get_col("
SELECT post_content FROM {$wpdb->posts}
WHERE post_status IN ('publish', 'draft', 'pending', 'future')
AND post_type IN ('post', 'page')
");
$used_urls = [];
foreach ($results as $content) {
preg_match_all('/<img[^>]+src=["\']([^"\']+)["\']/', $content, $matches);
if (!empty($matches[1])) {
$used_urls = array_merge($used_urls, $matches[1]);
}
}
return array_unique($used_urls);
}
✅ Step 2: Compare with Media Library and Find Unused
Now we get all image attachments and see which ones are not referenced.
function find_unused_images() {
$used_urls = get_used_image_urls();
$unused = [];
$attachments = get_posts([
'post_type' => 'attachment',
'post_mime_type' => 'image',
'numberposts' => -1,
'post_status' => 'inherit',
]);
foreach ($attachments as $attachment) {
$url = wp_get_attachment_url($attachment->ID);
// Check if file URL appears in any post content
if (!in_array($url, $used_urls)) {
$unused[] = $attachment;
}
}
return $unused;
}
✅ Step 3: Display or Delete Unused Images (Admin Page)
Here’s a simple admin interface to review and delete unused images:
function cleanup_unused_images_admin_page() {
if (!current_user_can('manage_options')) return;
echo '<div class="wrap"><h1>Unused Images</h1>';
$unused = find_unused_images();
if (isset($_POST['delete_unused']) && check_admin_referer('delete_unused_images')) {
foreach ($unused as $image) {
wp_delete_attachment($image->ID, true);
}
echo '<div class="updated"><p>Deleted ' . count($unused) . ' unused images.</p></div>';
$unused = [];
}
if (empty($unused)) {
echo '<p><strong>No unused images found.</strong></p>';
} else {
echo '<p><strong>' . count($unused) . ' unused images found.</strong></p>';
echo '<form method="post">';
wp_nonce_field('delete_unused_images');
echo '<input type="submit" name="delete_unused" value="Delete All Unused Images" class="button button-danger">';
echo '</form>';
echo '<ul>';
foreach ($unused as $img) {
echo '<li>' . esc_html($img->post_title) . ' – <code>' . esc_url(wp_get_attachment_url($img->ID)) . '</code></li>';
}
echo '</ul>';
}
echo '</div>';
}
function register_unused_images_menu() {
add_management_page('Unused Images Cleanup', 'Unused Images', 'manage_options', 'unused-images', 'cleanup_unused_images_admin_page');
}
add_action('admin_menu', 'register_unused_images_menu');
Summary
This custom solution gives you control over image clutter and improves:
Server performance
Backup size and speed
Media organization
You can adapt this method to:
Scan for featured images only
Include custom post types
Search within custom fields
Convenient hosting for your WordPress sites
Looking for good hosting for your WordPress sites? Pay attention to Host4Biz. It is a reliable hosting with modern servers in Europe and a Ukrainian team.
And with the promo code MYHOST10 you will get a 10% discount on your first payment. To do this, register here and enter the code before paying.
Note: There are affiliate links in the link given above and if you buy something, I’ll get a commission at no extra cost to you.
Read also: Best Digital Agency WordPress Themes (Mostly Free) https://medium.com/@wwwebadvisor/best-digital-agency-wordpress-themes-mostly-free-a4f64e0bd03f