Finding Orphan Files After Migrating From Another Cloud Plugin
Migrating to StaticQ Media from WP Offload Media, MediaCloud, or another cloud-storage plugin? Here's how to safely identify and clean up orphan files in your bucket — and why the Media Library Scanner is essential before you run Orphan Detection.
If your WordPress media library lives on local disk, the standard orphan-detection guide is the one you want. This post is for a different starting point: you previously used a plugin like WP Offload Media, MediaCloud, or similar to push your media to a cloud bucket (Cloudflare R2, AWS S3, etc.). Now you’re switching to StaticQ Media and you want to clean up orphan files — in your bucket, on local disk, or both.
This scenario has more moving parts than a clean local-only cleanup, and the order of operations matters. The short version: run the Media Library Scanner before Orphan Detection. The rest of this post explains why, walks through the most common starting configurations, and shows you what a healthy migration looks like.
This is a draft. Scenarios and code references are based on the StaticQ Media v3.3.3 codebase. If you spot something that doesn’t match your experience, let us know.
Why This Case Is Different
In the local-only case, the orphan scanner walks your wp-content/uploads/ directory and compares each file against an index built from WordPress’s _wp_attached_file and _wp_attachment_metadata postmeta. That’s it. The StaticQ record for each attachment contributes very little to the index — just two fields (original_location and has_webp), and both have safe defaults at registration time.
When your media is in a bucket, three new variables come into play:
-
How did your previous plugin record file paths? Some plugins (WP Offload Media is the most common) rewrite WordPress’s
_wp_attached_filepostmeta to a cloud-URL form likes3://bucket/path/photo.jpg. Others leave the postmeta as a normal relative path and intercept image URLs at output time only. The two approaches produce different starting states. -
Is StaticQ pointed at the same bucket as your previous plugin, or a fresh one? If it’s a fresh bucket, the bucket scan finds nothing — orphan detection becomes a local-only exercise. If it’s the same bucket, you’ll see every file your previous plugin uploaded, and StaticQ has to decide which are still legitimate.
-
Do your local files still exist? If your previous plugin ran in cloud-only mode, the local files may have been deleted after upload. Your
wp-content/uploads/directory might be nearly empty. That changes both what’s worth scanning and what counts as an orphan.
These variables interact, so let’s walk through them in pieces.
Why You Should Run the Media Library Scanner First
A freshly-registered StaticQ record carries some honest defaults — and a few fields that haven’t been verified yet. The most important ones from the orphan-detection point of view:
has_webp = 0for every record. Registration doesn’t know whether.webpvariants exist; it leaves the flag at zero. The orphan scanner treats.webpfiles as expected only when the record claims them, so a fresh-registration scan flags every existing WebP file as an orphan. On a site previously offloaded with WebP enabled, that can easily be thousands of false-positive entries.original_location = NULLfor image records with a scaled-original. The orphan scanner’s NULL branch safely indexes both possible locations (alongside the master and insideoriginals/), so legacy originals are never mis-flagged. Good default, no scanner pass required to make it safe — but the scanner does promote it to a concrete value so subsequent operations are more efficient.storage_location = 'local'for every record. This field is not read by the orphan scanner, so it can’t cause false positives. But it does affect what the URL rewriter does on the front-end, so leaving it un-aligned means StaticQ might not yet serve your already-offloaded images from the bucket until the scanner aligns the record.
The Media Library Scanner reads each record’s actual state — checks the file’s real location on disk, checks the bucket, looks for WebP siblings, verifies dimensions and sizes against current settings — and writes the truthful values back. After it runs, the StaticQ records describe reality, not assumptions.
Running Orphan Detection against aligned records gives you a tight, accurate list. Running it before alignment gives you a noisy list dominated by “WebP not yet verified” entries that you’d have to manually exclude. Always run the Media Library Scanner first when your starting state involves a pre-existing bucket.
The Recommended Sequence
For all the scenarios below, the high-level flow is the same:
- Deactivate the previous offload plugin. Don’t uninstall yet — some plugins offer a “restore local URLs” or “switch back to local” tool you may want to run first.
- Install and activate StaticQ Media.
- Configure StaticQ’s bucket connection if you want StaticQ to manage the bucket going forward. Point it at either your existing bucket (if you want to reuse what’s there) or a fresh one (if you want a clean slate).
- Register your media.
StaticQ > Media Manager > Register Unregistered Attachments. - Run the Media Library Scanner. Click
Run Scanner. This is the step that aligns each record to observed state. - Apply fixes the scanner offers. It will surface missing WebP variants (you can generate them), misplaced originals (it can move them), missing sizes (it can regenerate them), and storage-sync gaps (it can upload or download to align local and bucket).
- Then run Orphan Detection. Now you have an accurate index, and the orphan list reflects genuine orphans only.
- Review, exclude, quarantine, test, purge. Same safety-net workflow as the local-only guide.
The steps that change between scenarios are mostly Step 3 (which bucket?) and how much work Step 6 produces.
Scenario A: Previous Plugin Hijacked _wp_attached_file (e.g., WP Offload Media in S3-URL mode)
Starting state:
- WordPress postmeta
_wp_attached_filecontains values likes3://bucket-name/wp-content/uploads/2024/05/photo.jpg. - WordPress postmeta
_wp_attachment_metadatais usually intact, with normal relative paths in thefileandsizeskeys. - Local files may or may not exist depending on the previous plugin’s mode.
- Bucket has masters, sizes, possibly WebP variants, and possibly the unscaled originals.
What happens during registration:
StaticQ’s registration handler recognizes the cloud URL in _wp_attached_file and skips that attachment — no StaticQ record is created for it. This is intentional: registration is read-only and doesn’t try to repair postmeta written by another plugin. (We considered making this more aggressive but decided it would add complexity for an edge case better solved at the migration boundary; see our note on this lower down.)
What happens during the Media Library Scanner pass:
The scanner handles cloud-URL postmeta the same way the orphan indexer does: it recognizes common schemes (s3://, gs://, gsus://, do://) and extracts the WordPress year/month relative path. For these attachments, it can build a working state. For other schemes (https://, custom prefixes), the scanner may not be able to map them back to a relative path — those attachments will need manual intervention.
Recommendation: before deactivating WP Offload Media, run its built-in “Remove URLs” or “Restore local URLs” tool (it has slightly different names in different versions). This rewrites _wp_attached_file back to normal WordPress format. After that, StaticQ’s registration creates clean records for every attachment, and the Media Library Scanner can do its job in full.
Scenario B: Previous Plugin Left Postmeta Alone (e.g., Optimole, Smush offload in URL-rewrite mode)
Starting state:
_wp_attached_fileis a normal relative path._wp_attachment_metadatais intact.- Local files exist (these plugins typically don’t delete local copies).
- Bucket has masters, sizes, possibly WebP.
What happens during registration:
Clean — every attachment gets a StaticQ record. storage_location defaults to 'local', has_webp = 0, original_location = NULL.
What happens during the Media Library Scanner pass:
The scanner observes that files exist both locally and in the bucket and aligns storage_location to 'both' (or 'bucket' depending on what’s actually present). It looks for WebP variants and sets has_webp = 1 where it finds them. It verifies originals and aligns original_location accordingly.
Recommendation: this is the smoothest migration path. Deactivate the previous plugin, activate StaticQ, register, scan, fix any reported issues, then run Orphan Detection. The orphan list should be small and accurate.
Scenario C: Previous Plugin Ran in Cloud-Only Mode (Local Files Deleted)
Starting state:
- Local
wp-content/uploads/may be empty or near-empty. _wp_attached_fileis either hijacked (Scenario A) or normal (Scenario B), depending on the plugin.- Bucket has all the files.
What changes: The Media Library Scanner will report your local copies as missing. This is correct — they are. Depending on your storage mode preference, you have two paths:
- Keep StaticQ in cloud-only mode. The scanner won’t flag the missing local files as a problem; it will adjust
storage_locationto'bucket'. Orphan Detection then focuses on the bucket only. - Move to cloud_and_local mode. The scanner offers a “Download to Local” fix that pulls each file back from the bucket to local disk. This is a lot of disk activity but gives you a complete local copy. After it finishes, the records show
storage_location = 'both'.
Recommendation: decide your storage mode before running the scanner so the alignment fixes match your intent. Pick “Download to Local” only if you have the disk space and bandwidth quota to support it.
Scenario D: New Bucket, Migrating Files Across
Starting state:
- Files in the previous plugin’s bucket.
- StaticQ pointed at a new, empty bucket.
What changes: You need to move the files from the old bucket to the new one before any of this works. Two main approaches:
- Re-upload through StaticQ. If your local files exist, the Media Library Scanner’s “Upload to Bucket” fix sends them to your new bucket. Slow on a large library but uses only normal HTTP.
- Bucket-to-bucket copy outside WordPress. Use
rclone,aws s3 sync, or your cloud provider’s web console to copy files from the old bucket to the new one. Faster for large libraries.
After the files are in the new bucket and StaticQ is pointed at it, follow the standard sequence (register → scan → fix → orphan-scan).
Recommendation: if you’re going to do this migration anyway, this is your one chance to reorganize your bucket structure. StaticQ uses wp-content/uploads/ as the bucket prefix by default; if your previous plugin used a different scheme, copying into the StaticQ-expected layout from the start avoids confusion later.
What the Orphan Scanner Will Still Flag Incorrectly
Even with the Media Library Scanner pass done, a few categories of legitimate files can show up as orphans. Knowing them in advance helps you scan the result list productively:
- Files at non-WordPress path schemes. If your previous plugin uploaded files under paths that don’t follow WordPress’s
YYYY/MM/filenameconvention (some CDN plugins use account-prefixed paths likecdn-acct-123/photo.jpg), the orphan scanner won’t recognize them. They’ll appear in the report. Whether they’re “real” orphans is a judgment call; review them carefully before quarantining. - Files referenced only via page builders. Elementor, Beaver Builder, and similar tools sometimes embed image URLs directly in their saved layouts without creating proper attachment references. The Post Content Scanner (a separate StaticQ tool) catches some of these but not all. Use the folder-exclusion feature to skip page-builder folders if needed.
http://andhttps://postmeta hijacks. A small minority of offload plugins write full HTTP/HTTPS URLs into_wp_attached_fileinstead of a recognizable scheme. StaticQ’s path-extraction logic targets the commons3:///gs://patterns; arbitrary HTTP URLs may not parse cleanly. We’ve documented this as a known limitation rather than try to make the scanner repair arbitrary previous-plugin artifacts at scan time — better to clean up the postmeta at the migration boundary.
The safety-net workflow handles all of these: review the folder tree, exclude what looks legitimate, quarantine the rest, test the site, then purge.
A Note on “Just Running Orphan Detection After Registration”
You can skip the Media Library Scanner step if you treat the orphan list as a diagnostic rather than a cleanup-ready report. You’ll see WebP files flagged as orphans because their flag is still at zero. You’ll see misaligned storage_location showing files as “available locally” when they’re really bucket-only. The orphan tree gives you a view of “what StaticQ can confirm vs. what it can’t” at that moment.
That can be useful for assessment. It’s not useful for cleanup. If you intend to quarantine and purge based on the report, run the scanner first.
Safety Net: Quarantine, Test, Restore
Whatever path you take, the cleanup workflow ends the same way as the local-only flow:
- Quarantine the files you’ve decided are orphans. They’re moved to a separate directory, not deleted.
- Test your site thoroughly — homepage, recent posts, WooCommerce products, page-builder layouts, downloads.
- Restore anything that turned out to be wrongly flagged. One click per file.
- Purge the quarantine when you’re confident.
Migrations have more variables than local-only cleanup, so give yourself more time at the test step. A week between quarantine and purge is reasonable on a high-traffic site.
Summary Checklist
For most migrations, in order:
- Run the previous plugin’s “restore local URLs” tool if it has one
- Deactivate the previous plugin (don’t uninstall yet — you may need to re-enable it briefly if something breaks)
- Install and activate StaticQ Media
- Configure StaticQ’s bucket connection (same bucket or new bucket — decide ahead of time)
- Register your library
- Run the Media Library Scanner
- Apply the scanner’s fixes (WebP, originals, sizes, storage sync)
- Run Orphan Detection on the appropriate scope (local, bucket, or both)
- Review the folder tree carefully — bucket migrations have more false-positive shapes than local-only
- Exclude page-builder and plugin folders
- Quarantine, test, restore as needed, purge