First 5: I set them upSee
·SafeGuard Team

Why Elementor Pages Still Point at Your Old Domain After a Search and Replace

A normal search and replace misses Elementor's URLs because they are JSON inside a serialized array, with the slashes escaped. Here is what actually breaks, why the obvious fix corrupts the row, and how to check whether it happened to you.

You moved the site. You ran a search and replace across the database, swapped the old domain for the new one, and the front page looks right. Then you open a page built with Elementor and it is still loading images from the old domain, or the editor throws an error, or the layout is gone entirely.

The replace did run. It just never saw those URLs.

Elementor does not store URLs the way the rest of WordPress does

Most plugins store a URL as a plain string in an option or a meta field. A search and replace finds it, swaps it, and you are done.

Elementor stores an entire page layout as JSON, and that JSON is stored inside a PHP-serialized value. When PHP or WordPress encodes that JSON, / is escaped as \/ by default. So the URL sitting in your database does not look like this:

https://oldsite.com/wp-content/uploads/hero.jpg

It looks like this:

https:\/\/oldsite.com\/wp-content\/uploads\/hero.jpg

Search for https://oldsite.com and you will not find it, because that string is not there. The replace reports success, changes hundreds of rows elsewhere, and leaves every Elementor page pointing at the old domain.

Why the obvious fix makes it worse

The natural next thought is to search for the escaped form instead. Run a replace on https:\/\/oldsite.com and it will match.

It will also break the row, if the tool doing it is not serialization-aware.

PHP serialization records the byte length of every string ahead of the string itself. A value looks like this:

s:19:"https://oldsite.com";

That 19 has to equal the exact byte count. Replace a 19-character domain with a 22-character one and the prefix still says 19. PHP's unserialize() then reads 19 bytes, finds the data does not end where it was promised, and returns false. WordPress gets nothing back. Depending on which field it was, you get an empty layout, a fatal error in the editor, or a page that renders as raw text.

This is the failure people describe as "the search and replace corrupted my database". The replace was correct as text. It was invalid as serialized PHP.

So the escaped-slash problem and the serialization problem stack: you have to find the URL in its escaped form, and you have to rewrite the containing structure so that every length prefix is recalculated.

What a correct replace has to do

Handling this properly means walking the value rather than treating it as text:

  1. Detect the format. Is this plain text, serialized PHP, JSON, or JSON nested inside serialized PHP? Elementor is the last one, and it is the one text-level tools miss.
  2. Unserialize it into a real PHP structure, rather than pattern-matching the string.
  3. Walk the whole tree, including arrays inside arrays and objects inside those.
  4. Decode the JSON leaves, replace inside them, and re-encode.
  5. Preserve the original escaping style. This is the step that is easy to get wrong. If the stored value used \/ and you re-encode with unescaped slashes, every URL in that field changes length. The content is right and the byte count is different, which puts you back in the corruption case you were trying to avoid.
  6. Re-serialize, letting PHP compute fresh length prefixes.

Step 5 is worth dwelling on because it is invisible until it is not. Two JSON encodings of the same data are both valid JSON and different lengths. A tool can decode, replace correctly, re-encode correctly, and still hand back a value that no longer matches its own length prefix.

Check whether it happened to you

Before you change anything, confirm the diagnosis. Look for the escaped form directly:

SELECT COUNT(*) FROM wp_postmeta
WHERE meta_key = '_elementor_data'
  AND meta_value LIKE '%oldsite.com%';

_elementor_data is where Elementor keeps the page layout. If that count is above zero after you have already run a replace, the replace missed them, and the escaped slashes are why.

Change the table prefix if yours is not wp_, and run it against a copy if you can.

Fixing it

Whatever tool you use, the requirements are the same: it has to unserialize rather than pattern-match, decode JSON leaves, preserve slash escaping on re-encode, and it should let you see what it is about to change before it writes.

That last point is not a nicety. Every step above is a place where a subtle mistake produces a database that looks fine until someone opens a page. A preview that shows you the actual before and after for representative matches is how you catch it while it is still cheap.

Lucid Search & Replace is a free plugin on WordPress.org that does this. It walks serialized structures, decodes the JSON layer that Elementor uses, preserves the original slash escaping when re-encoding, and shows you a visual preview of representative matches before anything is written. It can take a snapshot first and undo unchanged cells afterwards. The preview and the undo are not held behind an upgrade.

If you are migrating the whole site rather than fixing one that already moved, SafeGuard does the move end to end, and there is a step-by-step guide to moving a WordPress site if you want to see what that involves first. SafeGuard is a separate paid product, not an upgrade to the free plugin.

The short version

  • Elementor stores layouts as JSON inside serialized PHP, with / escaped to \/.
  • A plain search for https://oldsite.com will not match, so the replace silently skips it.
  • Searching for the escaped form and replacing it as text corrupts the row, because PHP serialization stores byte lengths that no longer match.
  • A correct fix unserializes, walks the tree, decodes and re-encodes the JSON leaves, preserves the original escaping, and re-serializes.
  • Check wp_postmeta where meta_key = '_elementor_data' to confirm you are in this case.