How to Find and Delete Orphaned Uploads in WordPress

A couple of nights ago, my server monitoring fired off a disk space alert at 2 AM. When you have four kids waking up in a few hours, the last thing you want is a partition running low on space.

I hopped into SSH, ran ncdu /var/www/html/wp-content/uploads, and stared at the screen. The uploads directory had climbed to over 18 gigabytes, but the WordPress Media Library only showed about 800 images. Where did the rest come from?

If you run WordPress sites long enough—especially WooCommerce stores or client blogs migrated across hosts—you will hit this exact issue. Your disk fills with “ghost files” that WordPress does not even know exist.

Here is what causes them, why deleting files over FTP blindly is risky, and how we clean them up safely.


What Exactly Is an “Orphaned Upload”?

To understand why files get lost, you have to look at how WordPress tracks media under the hood.

When you upload an image in the WordPress admin:

  1. The physical file gets written to disk inside /wp-content/uploads/YYYY/MM/.
  2. WordPress generates various sub-sizes (thumbnail, medium, large) on disk.
  3. WordPress creates an entry in your database inside the wp_posts table with post_type = 'attachment'.
  4. The attachment dimensions and sub-size filenames are stored inside wp_postmeta under _wp_attachment_metadata.

An orphaned upload happens when physical files sit in your uploads directory, but the database has zero record of them.

/wp-content/uploads/
   ├── 2024/05/product-photo.jpg         <-- In wp_posts (Active Media)
   ├── 2024/05/product-photo-300x200.jpg <-- Registered sub-size (Safe)
   └── 2024/05/abandoned-banner.jpg      <-- No DB record (Orphan / Ghost File)

How does this happen? Usually:

  • An upload stalled halfway through (browser crashed or PHP timed out).
  • A media item was deleted from the admin, but a file permission glitch prevented PHP from unlinking the physical file on disk.
  • A backup, staging import, or uninstalled plugin left temp files behind.
  • You changed themes, and old custom image dimensions were abandoned on disk forever.

These ghost files do not just eat storage. Every time you run daily backups or rsync your site to a staging server, you are transferring dead weight.


Why You Shouldn’t Just rm -rf or FTP Delete Blindly

Back in my early twenties tinkering with 486 PCs and early Linux distros, I had a bad habit of running aggressive cleanup commands. I learned the hard way that brute force usually ends in reinstalling everything on a Saturday night.

You cannot simply sort files by date over FTP and delete old ones. An image uploaded back in 2018 might still be the primary logo on your homepage, or the featured image on your top-ranking blog post.

You need a tool that physically walks the uploads directory on disk, cross-references each file against active WordPress attachment records (including all registered thumbnails), and flags only the true orphans.


Why We Built CrushEdge Orphaned Upload Cleaner

Most media cleanup plugins on the repository fall into two categories:

  1. They try to do too much—scanning every post content block, CSS file, and external CDN—which causes PHP timeouts and white screens on shared hosting.
  2. They choke when you try to bulk delete more than 25 files at once.

We built CrushEdge Orphaned Upload Cleaner (v1.3) to be focused, safe, and fast. It does one specific job: it finds files sitting in wp-content/uploads/ that have zero attachment records in the WordPress database, lists them in a clean table, and lets you quarantine or delete them in batches without crashing your server.

Here is what we baked into it:

  • Trash Can Safety Quarantine: Instead of immediately destroying files, the default action safely quarantines them into a protected folder (.crushedge-trash/) locked down with .htaccess and web.config.
  • 1-Click Single & Bulk Restore: If you ever discover that an ignored plugin needed a quarantined file, restore individual files, selected batches, or all files back to their exact original upload locations with a single click.
  • WordPress-Style Row Actions: Clean contextual actions appear under filenames on hover (Move to Trash | Delete Permanently | Ignore Folder), keeping the main action column uncluttered.
  • Sticky Table Headers: Column headers stay pinned at the top of your screen as you scroll down through 100 or 200 files.
  • 1-Click “Copy Path” Badges: Click any relative path badge (uploads/...) to copy it straight to your clipboard with animated feedback.
  • “Only Orphan” view by default: As soon as you open the page, you only see the files that can be safely cleaned up.
  • Fast column sorting: Sort by file size so you can target massive multi-megabyte videos and raw PNGs first.
  • Smart thumbnail & derivative resolver: Automatically detects auto-generated thumbnails (-150x150, -scaled, -e123...) and Next-Gen WebP/AVIF companions linked to active Media Library attachments, shielding them from false detection.
  • Chunked batch processing: Instead of sending 100 operations in one giant request that hits PHP execution limits, it processes in small, rapid 10-file asynchronous chunks.
  • Fixed-viewport progress bar: Even if you display 100 items per page and scroll down, the live progress modal stays centered right in front of you.
  • Instant stop/cancel: If you change your mind mid-batch, clicking Stop cancels the request immediately.
  • Strict safeguards: Core files like .htaccess, index.php, web.config, and dynamic directories (Elementor styles, cache) are protected automatically.

Step-by-Step: How to Safely Clean Up Your Uploads

Let’s walk through cleaning up your uploads directory safely.

Step 1: Take a Quick Backup

Before you delete anything on a production server, always take a backup. I tell this to everyone just like my dad told me to disconnect the car battery before touching the alternator.

Use your hosting backup tool, UpdraftPlus, or run a quick tarball via terminal:

tar -czf uploads-backup-$(date +%F).tar.gz wp-content/uploads/

Step 2: Open CrushEdge Orphan Cleaner

Install and activate the plugin (crushedge-orphaned-upload-cleaner), then navigate to Tools → CrushEdge Orphan Cleaner in your WordPress dashboard.

The plugin scans your uploads directory and checks every file against your wp_posts and _wp_attachment_metadata tables.

The top summary cards will show:

  • Total files on disk and total disk usage.
  • Total orphaned files found and recoverable space.
  • Files currently quarantined in the Trash Can.

Step 3: Sort by Size

By default, the Only Orphan switch is already toggled on.

Click the Size column header to sort in descending order. In most cases, 80% of your wasted space comes from a handful of uncompressed camera uploads, accidentally uploaded video clips, or failed ZIP archives.

Step 4: Quarantine Files to the Trash Can

Select the orphaned files you want to clean up:

  1. Select one or more files using the checkboxes, or check the header checkbox to select all on the page.
  2. Choose Move to Trash from Bulk actions and click Apply (or click the Move to Trash button on an individual row).
  3. The chunked batch progress modal will move the files into the protected .crushedge-trash/ directory.

The files are now immediately taken offline and isolated, but they are not lost!

Step 5: Review & Restore or Permanently Purge

Click the Trash Can tab at the top of the screen to inspect all quarantined items.

  • Need a file back? Click Restore next to any item, or select multiple files and click Restore Selected (or Restore All) to put them back in their exact original folder in wp-content/uploads/.
  • Ready to free disk space permanently? Click Empty Trash Can (or select items and choose Permanently Delete). The plugin will safely purge the files from disk and reclaim your storage.

A Quick Note on File Permissions

If a file fails to delete during the process, it is almost always a Linux file permission mismatch. This happens often if you previously extracted a tar archive as root instead of the web server user (www-data or nginx).

If you run your own VPS, make sure your uploads folder is owned by the web server:

sudo chown -R www-data:www-data /var/www/html/wp-content/uploads/
sudo find /var/www/html/wp-content/uploads/ -type f -exec chmod 644 {} \;
sudo find /var/www/html/wp-content/uploads/ -type d -exec chmod 755 {} \;

Wrapping Up

Cleaning out orphaned files is one of the easiest ways to speed up your backups, keep hosting costs down, and maintain a tidy server.

You can grab CrushEdge Orphaned Upload Cleaner (v1.3) from the WordPress Plugin Directory under the slug crushedge-orphaned-upload-cleaner, or see below.

If this plugin saved your disk space and you’d like to buy me a coffee to support continued free development, feel free to visit our Ko-fi page.

Got questions or ran into an unusual file type? Leave a comment below and I will be glad to help troubleshoot.


Download

No Comments

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.