Skip to main content

wp search-replace

Overview

Find and replace any string across all WordPress database tables — including inside PHP serialized data. The essential command for URL migrations, domain changes, and content updates.

What It Does

wp search-replace scans every row and column across all (or specified) WordPress tables, finds strings matching the search term, and replaces them with the replacement string. Critically, it handles PHP serialized data correctly — updating byte-length counts so serialized arrays and objects remain valid after replacement.

Syntax

wp search-replace <old-string> <new-string> [<table>...] [OPTIONS]

Arguments & Options

Argument / FlagDefaultDescription
OLD_STRINGString to find
NEW_STRINGString to replace with
TABLE...All tablesSpecific tables to limit search to
--dry-runPreview what would change without writing
--skip-columns=COLSSkip specific columns (comma-separated)
--include-columns=COLSOnly search in these columns
--all-tablesInclude all tables, not just with WP prefix
--networkRun on all sites in a multisite network
--preciseUse regex-capable matching (slower)
--recurse-objectsHandle nested serialized PHP objects
--no-reportSuppress results table output
--report-changed-onlyOnly show tables where replacements were made
--format=FORMATtableOutput format: table, json, csv, yaml
--export=FILEExport SQL instead of writing to DB
--export_insert_size=N50Number of inserts per SQL query when exporting

Basic Usage

Domain migration (the most common use case)

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

Always preview first with --dry-run

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

Dry-run output:

+------------------+-----------+--------------+------+
| Table | Column | Replacements | Type |
+------------------+-----------+--------------+------+
| wp_options | option_value | 3 | SQL |
| wp_posts | post_content | 47 | SQL |
| wp_postmeta | meta_value | 12 | PHP |
+------------------+-----------+--------------+------+
Success: 62 replacements to be made.

Skip the guid column (WordPress best practice)

wp search-replace 'http://old.com' 'https://new.com' --skip-columns=guid
Always skip the guid column

The guid column in wp_posts is used as a permanent unique identifier for posts. WordPress documentation recommends not changing it even during migrations.

Real-World Scenarios

Scenario 1: Production → Staging migration

# 1. Import production dump to staging
wp db import prod_dump.sql

# 2. Preview the replacements
wp search-replace 'https://example.com' 'https://staging.example.com' --dry-run --skip-columns=guid

# 3. Apply
wp search-replace 'https://example.com' 'https://staging.example.com' --skip-columns=guid

# 4. Flush cache
wp cache flush

Scenario 2: HTTP to HTTPS migration

# Dry run
wp search-replace 'http://example.com' 'https://example.com' --dry-run --skip-columns=guid

# Apply
wp search-replace 'http://example.com' 'https://example.com' --skip-columns=guid
wp cache flush

Scenario 3: Change site path (moved to subdirectory)

wp search-replace 'https://example.com' 'https://example.com/blog' --skip-columns=guid

Scenario 4: Multisite Network search-replace

# Replace across all network sites
wp search-replace 'old-network.com' 'new-network.com' --network --skip-columns=guid

Scenario 5: Export replacements as SQL (without applying)

wp search-replace 'http://old.com' 'https://new.com' --export=migration.sql --skip-columns=guid
# Review migration.sql, then apply:
wp db import migration.sql

Best Practices

  1. Always run --dry-run first — see exactly what will change before committing.
  2. Always use --skip-columns=guid — guid is a permanent post identifier.
  3. Back up with wp db export before any search-replace on production.
  4. Include protocol in strings — replace http://example.com, not just example.com, to avoid partial matches.
  5. Flush cache afterwp cache flush clears stale cached values.
  6. Use --report-changed-only to quickly identify which tables were affected.

Troubleshooting

ProblemCauseFix
Serialized data broken after manual SQL UPDATEUsed raw SQL instead of wp search-replaceUse wp search-replace — it fixes byte counts
URLs partially replacedString included protocol but DB has mixed http/httpsRun both: first http://, then https://
Site still loads old domainObject cache not clearedRun wp cache flush
Command too slow on large DBMany tables or large dataAdd --table to limit scope; run during off-peak
Dry-run shows 0 replacementsSearch string not in DBVerify string with wp db query "SELECT ..."

Quick Reference

wp search-replace 'http://old.com' 'https://new.com' --dry-run --skip-columns=guid
wp search-replace 'http://old.com' 'https://new.com' --skip-columns=guid
wp search-replace 'old' 'new' --network --skip-columns=guid
wp search-replace 'old' 'new' --export=changes.sql
wp cache flush

Next Steps