Search and Replace Without Breaking Serialized Data in WordPress
You changed your domain, moved to HTTPS, or migrated to a new host, and now you need to update thousands of old URLs scattered across your WordPress database. The obvious instinct is to run a REPLACE() query in phpMyAdmin or a quick find-and-replace. Don't. That single command is the most common way people white-screen their own site — because a lot of what's in your database isn't plain text. It's serialized data, and a naive replace silently corrupts it.
This post explains what serialized data actually is, why a raw SQL replace breaks it, and the safe workflow that lets you do the same search-and-replace without the risk.
What serialized data is in WordPress
WordPress stores a lot of structured information — not just strings, but arrays and PHP objects — inside single database columns. To squeeze a whole array into one text field, PHP "serializes" it: it flattens the structure into a string that records the type and length of every value, so it can be rebuilt later.
You'll find serialized blobs all over a typical site:
- Options (
wp_options) — theme settings, plugin config, transients. - Postmeta (
wp_postmeta) — page-builder layouts (Elementor, Beaver Builder, WPBakery), custom fields, ACF data. - Widgets and theme mods — sidebar contents and customizer settings.
- Usermeta — per-user preferences.
Here's the key detail. A serialized string isn't just the value — it's the value plus its character count. A simple serialized string looks like this:
s:5:"hello";
Read that as: a string (s), 5 characters long, with the value hello. The 5 is a length prefix, and PHP trusts it absolutely. When it unserializes, it reads exactly 5 characters. Inside a serialized array, dozens of these length prefixes stack up:
a:1:{s:7:"site_url";s:21:"http://oldsite.com/wp";}
That s:21 says the URL value is exactly 21 characters long. Remember that number — it's where everything goes wrong.
For a deeper walkthrough, see How to update serialized data effectively.
Why a naive SQL REPLACE() corrupts it
Now imagine you migrate from oldsite.com to newsite.io and run this in phpMyAdmin:
UPDATE wp_options SET option_value = REPLACE(option_value, 'oldsite.com', 'newsite.io');
SQL does exactly what you asked: it swaps the text. The value http://oldsite.com/wp (21 characters) becomes http://newsite.io/wp (20 characters). But look at what it didn't touch:
a:1:{s:7:"site_url";s:21:"http://newsite.io/wp";}
The prefix still says s:21, but the string is now only 20 characters long. PHP reads 21 characters as instructed, runs past the end of the value into the next part of the blob, and the whole structure fails to unserialize. The data is now garbage.
The symptoms are immediate and scary:
- White screen of death when WordPress tries to load a corrupted option.
- Missing widgets and empty sidebars.
- Broken page-builder content — Elementor or WPBakery sections render blank or throw errors, because their entire layout lived in a serialized postmeta blob.
- Theme settings reset to defaults.
This isn't a phpMyAdmin bug. It's a fundamental mismatch: a text-level REPLACE() changes string lengths without updating the length prefixes that PHP depends on. Any tool that operates on the raw text — SQL queries, manual find-and-replace, a careless export/import — has the same flaw. If you've ever searched "search replace broke my site," this is almost always the reason.
How a serialized-aware tool fixes this
The fix isn't to avoid search-and-replace. It's to do the replacement at the right layer. A serialized-data-aware tool doesn't treat the blob as text. Instead it:
- Unserializes the blob back into a real PHP array or object.
- Replaces your search term inside the actual values.
- Re-serializes the structure, recomputing every length prefix so each one matches its new value.
After that process, the same record comes out correct:
a:1:{s:7:"site_url";s:20:"http://newsite.io/wp";}
Notice the prefix is now s:20, matching the 20-character URL. PHP unserializes it cleanly, and nothing breaks. This is the difference between "find and replace the characters" and "find and replace the meaning" — and it's why a plugin built for this job is safe where a raw SQL query is not.
Update URLs handles serialized data this way by default, so you don't have to know which columns contain blobs and which are plain text — it detects and re-serializes correctly across all of them.
The safe workflow with Update URLs
Knowing why it breaks is half the battle. Here's the workflow that removes the risk entirely. The order matters.
1. Back up first
Before any replace touches the database, take a backup. Update URLs includes a built-in one-click database backup so you don't have to leave the screen or wrangle phpMyAdmin. A backup means worst case is "restore and try again," not "rebuild the site." See How to do one-click database import and export.
2. Run a dry run
Never let your first replace be the live one. A dry run scans the database and reports exactly how many rows in which tables would change — without writing anything. This is your chance to catch a too-broad search term (replacing site.com when you meant oldsite.com) before it does damage. Walkthrough: How to do a dry run before search and replace.
3. Review the matches
Read the dry-run report properly. Confirm the affected tables make sense (you'd expect wp_options, wp_postmeta, and so on) and that the match count is in the range you expected. If a replace claims it'll touch tens of thousands of rows you didn't anticipate, stop and narrow the search. General reference: How to do search and replace text in WordPress.
4. Selectively apply
You don't have to run everything at once. Apply the replace to the tables you've verified, leaving sensitive ones (like GUIDs) out unless you specifically intend to change them. On GUIDs in particular — they're identifiers, not links, and usually shouldn't be rewritten. See What is GUID and how to replace.
5. Undo if needed
If something still looks off after applying, Update URLs gives you one-click Undo / Rollback to reverse the last operation. Combined with the backup from step 1, you have two independent safety nets. The goal is that no replace is ever a point of no return.
How to spot and recover from serialized corruption
If you (or a previous SQL run) already broke something, here's how to tell and what to do.
Signs your serialized data is corrupted:
- A blank white page on the front end or in
wp-admin. - Widgets, menus, or customizer settings that vanished after a migration.
- Page-builder content showing as empty or "this content failed to load."
- PHP errors in your debug log mentioning
unserialize()or "offset" warnings.
How to recover:
- Restore your backup. If you took one (you should have), this is a few clicks and the fastest fix — see the import/export guide.
- Use Undo if the corruption came from an Update URLs operation.
- Re-run the replace correctly with a serialized-aware tool and a dry run, so the lengths are recomputed properly this time.
- If you're stuck, the troubleshooting guide covers the common failure cases and how to clear them.
The broader, end-to-end playbook lives in our pillar guide: WordPress search and replace: the safe guide. If you're specifically changing domains, see Move WordPress to a new domain, and if you're coming from another tool, the Better Search Replace alternative covers the migration.
Conclusion
Serialized data is the reason "just run a quick REPLACE in phpMyAdmin" turns into a support ticket. The data isn't plain text — it's structured values with length prefixes PHP refuses to ignore, and changing a string's length without updating its prefix breaks the whole blob. The fix isn't to fear search-and-replace; it's to do it at the serialization layer with a tool that unserializes, replaces, and recomputes lengths for you. Back up, dry-run, review, apply selectively, and keep Undo in your pocket. Done that way, even a site-wide URL change is a routine, reversible task rather than a gamble.
FAQs
What is serialized data in WordPress?
It's a way PHP stores arrays and objects as a single string, recording the type and character length of every value (for example s:5:"hello"). WordPress uses it heavily in options, postmeta, widgets, and theme settings — including page-builder layouts.
Why did phpMyAdmin break my site after a search and replace?
Because REPLACE() swaps the text but doesn't update the length prefixes inside serialized blobs. When the new string is a different length than the prefix claims, PHP can't unserialize it, which causes white screens, missing widgets, and broken page-builder content.
Is a plugin search-and-replace safe for serialized data?
Yes, when the plugin is serialized-aware. Update URLs unserializes each blob, performs the replacement on the real values, and re-serializes with corrected length prefixes — so the structure stays valid. A raw SQL query does not do this.
How do I know if my serialized data is corrupted?
Look for white screens, vanished widgets or menus, empty page-builder sections after a migration, or unserialize() warnings in your debug log. These almost always trace back to a text-level replace that changed string lengths.
Can I undo a search and replace if it goes wrong?
Yes. Update URLs offers one-click Undo / Rollback to reverse the last operation, and a built-in one-click backup you can restore from. Between the two, you always have a way back. See the troubleshooting guide if you need help.