Guide Databases & MySQL

How to search and replace inside a database

A plain find-and-replace can silently corrupt serialized data — here is how to do it without breaking the site.

Updated 8 min read Intermediate

The most common reason to search and replace inside a database is a domain change: a site that moved from a staging URL to a live one, switched from http:// to https://, or moved to an entirely new domain, with the old address baked into thousands of rows across the database. It looks like a simple text substitution. In a WordPress database specifically, it is not quite that simple, and doing it the naive way is one of the more common causes of a site that looks broken immediately after a migration.

Always back up first

This is not reversible without a backup

A search-and-replace across a live database changes data in place. If something goes wrong — a badly scoped query, an unexpected match, a broken serialized value — there is no undo. Export the database before you start. See exporting a database.

Why a plain SQL replace can break WordPress

WordPress stores some settings and widget data as PHP "serialized" strings — a text format that encodes not just the value but the exact byte length of each string within it. A serialized value looks something like this:

a:1:{s:16:"http://old-site.com";s:0:"";}

The 16 in that string is not decorative — it is the exact character length of http://old-site.com that follows it. If a plain SQL REPLACE() changes the URL to something a different length, the string is now a different length than the number says it is, and PHP can no longer parse it. The visible result ranges from a blank widget to a broken settings page to, in worse cases, page content silently disappearing on the front end. The data is not gone, technically — it is just no longer readable by anything that expects valid serialized PHP.

Safe for plain text columns: SQL REPLACE with a WHERE clause

For columns that are just plain text — most commonly wp_posts.post_content, which holds post and page content — a direct SQL replace is safe, because there is no length prefix involved:

UPDATE wp_posts
SET post_content = REPLACE(post_content, 'http://old-domain.com', 'https://new-domain.com')
WHERE post_content LIKE '%old-domain.com%';
Why the WHERE clause is there, and why it matters

The WHERE clause above is not strictly required for REPLACE() to work correctly — REPLACE() only changes text that actually matches, and leaves other rows untouched either way. It is there because scanning every row in a large table when only a fraction contain a match wastes time, and because it makes the statement's intent explicit rather than relying on REPLACE() quietly doing the right thing. As a general habit, never run an UPDATE against a live table without a WHERE clause that limits it to the rows you actually mean to change — an UPDATE with no WHERE clause at all changes every row in the table, which is rarely what you want and is very difficult to undo without a backup.

Run this on a copy first if you are not confident, and always run it against a table you have already backed up. This approach is fine for post content, but it does not touch serialized data in other tables at all, which is exactly the gap the next section covers.

Safe for everything, including serialized data: WP-CLI

For a WordPress database, the tool built specifically to handle this correctly is WP-CLI's search-replace command. It understands PHP's serialization format, recalculates the length prefixes as it goes, and by default runs against every table in the database rather than just one:

wp search-replace 'http://old-domain.com' 'https://new-domain.com' --all-tables

Add --dry-run first to see what it would change without actually changing anything:

wp search-replace 'http://old-domain.com' 'https://new-domain.com' --all-tables --dry-run

This requires SSH access and WP-CLI installed, which is available on some hosting tiers and standard on a VPS or dedicated server. See using WP-CLI if you have not used it before.

Without SSH access

If WP-CLI is not available to you, a dedicated migration or search-replace plugin, installed temporarily on the site, does the same serialization-aware job through the WordPress admin area rather than the command line. Either way, the underlying principle is the same: plain text can be replaced directly and safely, and anything that might be serialized needs a tool that understands the format rather than a blind string substitution.

After the replace

Check the site thoroughly — widgets, theme options, any page built with a page builder, since these are the areas most likely to store serialized data. If something looks broken that was not broken before, restore from the backup you took at the start rather than trying to patch the broken serialized value by hand.

If this search-and-replace is part of a full domain change rather than an isolated fix, moving a site to a new domain covers the rest of what needs to change alongside the database.

Related reading