The Mistake
When archiving the Portland Winter Light Festival website to a new subdomain (2026.pdxwlf.com), I performed a standard database migration (Search & Replace) to update the URLs. I assumed that since my media lived in an AWS S3 bucket via WP Offload Media, the links would just follow the new domain.
The Problem
Everything looked broken. Images showed alt-text instead of logos, and the paths were pointing to local directories that didn’t exist instead of the S3 bucket.
Why it Occurred:
- Serialized Metadata: WP Offload Media doesn’t just change a URL; it stores a specific “handshake” in the
postmetatable. A standard search-and-replace often misses these serialized strings or breaks them. - Object Versioning: I had “Object Versioning” turned on in S3. This adds a random timestamp folder (like
/15152023/) to the S3 path. My database only knew the “clean” path, so when the plugin “unhooked,” the site had no way of guessing those random version numbers. - Function Failure: The standard WordPress function
attachment_url_to_postidfailed because it couldn’t map the new subdomain URLs back to the original Media IDs.
The Fix
I had Gemini write a couple custom PHP scripts to run via WP-CLI. Instead of trying to guess URLs, the script:
- Scanned the page content for filenames.
- Stripped out thumbnail dimensions (e.g.,
-825x250). - Queried the database for the Attachment ID based purely on that filename.
- Asked the Media Library for the real S3 path for that ID and swapped it back into the content.
You can find the full scripts at https://github.com/grayayer/Subdomain-Migration-AWS-Media-Repair-Scripts-for-WordPress
Lesson Learned
When S3 versioning is involved, don’t trust a Search & Replace. Trust the IDs.
