Changing URLs in WordPress databases manually destroys serialized data, breaks widget settings, corrupts option values, and leaves your site unusable. MySQL find-replace queries seem simple until you realize WordPress stores array lengths in serialized strings—one wrong replacement breaks everything.
WP-CLI’s search-replace command handles serialized data automatically, validates replacements before executing, and shows exactly what changed. It’s the only safe way to modify WordPress database content at scale.
In this guide, you’ll master WP-CLI search-replace for site migrations, domain changes, protocol updates, and any database modification requiring precision and safety.
Why WP-CLI Search-Replace is Critical
WordPress serialized data contains length values that break when modified with simple find-replace operations.
Problems with Manual Database Search-Replace
Serialized data corruption: Direct MySQL replacement breaks array and object serialization.
Partial replacements: URLs in JSON, meta fields, and options get missed.
No validation: Can’t preview changes before executing them.
No reporting: Don’t know what was changed or how many replacements occurred.
Irreversible: Once executed, can’t undo without restoring backups.
WP-CLI Search-Replace Advantages
Serialization-safe: Automatically recalculates lengths in serialized strings.
Dry-run mode: Preview all changes before executing them.
Comprehensive: Searches all tables, columns, and data types.
Detailed reporting: Shows exactly what will change and where.
Table-specific: Target specific tables or exclude sensitive data.
According to WordPress migration studies, 80% of failed migrations are due to incorrect URL replacement breaking serialized data.
Search-Replace Fundamentals
Master basic search-replace operations safely.
Basic Search-Replace Syntax
# Basic replacement
wp search-replace 'old-text' 'new-text'
# Replace in specific tables
wp search-replace 'old' 'new' wp_posts wp_postmeta
# Replace in all tables (dangerous, use carefully)
wp search-replace 'old' 'new' --all-tables
# Skip specific columns
wp search-replace 'old' 'new' --skip-columns=guidAlways Use Dry-Run First
# Preview changes without executing (CRITICAL!)
wp search-replace 'oldsite.com' 'newsite.com' --dry-run
# Dry-run with detailed report
wp search-replace 'http://' 'https://' --dry-run --report
# See exact replacement count
wp search-replace 'old' 'new' --dry-run --report --preciseCritical Rule: NEVER run search-replace without --dry-run first. Always preview changes.
Understanding Serialized Data
# WordPress serialized data example:
# a:3:{s:4:"name";s:4:"John";s:3:"age";i:30;}
# ^ length ^ length
# Direct MySQL replacement breaks this:
# UPDATE wp_options SET option_value =
# REPLACE(option_value, 'John', 'Jonathan');
# Result: a:3:{s:4:"name";s:8:"Jonathan";...}
# ^ WRONG! Should be s:8
# WP-CLI handles this automatically:
wp search-replace 'John' 'Jonathan' wp_options
# WP-CLI recalculates: s:4 becomes s:8 automaticallyLearn more about WP-CLI search-replace documentation.
Common Search-Replace Use Cases
Handle the most frequent WordPress database modification scenarios.
Domain Migration
#!/bin/bash
# migrate-domain.sh - Complete domain change
OLD_DOMAIN="oldsite.com"
NEW_DOMAIN="newsite.com"
# Backup first (ALWAYS!)
wp db export backup-before-domain-change.sql.gz
# Dry-run to preview
echo "Previewing changes..."
wp search-replace "https://$OLD_DOMAIN" "https://$NEW_DOMAIN" --dry-run --report
# Ask for confirmation
read -p "Proceed with replacement? (y/n) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Operation cancelled"
exit 0
fi
# Execute replacements
echo "Executing replacements..."
# Replace full URLs
wp search-replace "https://$OLD_DOMAIN" "https://$NEW_DOMAIN"
wp search-replace "http://$OLD_DOMAIN" "https://$NEW_DOMAIN"
# Replace protocol-relative URLs
wp search-replace "//$OLD_DOMAIN" "//$NEW_DOMAIN"
# Update WordPress options directly
wp option update home "https://$NEW_DOMAIN"
wp option update siteurl "https://$NEW_DOMAIN"
# Flush caches
wp cache flush
wp rewrite flush
echo "✓ Domain migration complete"HTTP to HTTPS Migration
#!/bin/bash
# migrate-to-https.sh
DOMAIN="example.com"
# Backup
wp db export backup-before-https.sql.gz
# Dry-run
wp search-replace "http://$DOMAIN" "https://$DOMAIN" --dry-run
# Execute
wp search-replace "http://$DOMAIN" "https://$DOMAIN"
# Update options
wp option update home "https://$DOMAIN"
wp option update siteurl "https://$DOMAIN"
echo "✓ Migrated to HTTPS"Use Case: Essential for SEO and security—Google requires HTTPS for ranking.
Path Changes (Subdirectory Moves)
# Move from subdirectory to root
wp search-replace 'https://example.com/blog' 'https://example.com'
# Move from root to subdirectory
wp search-replace 'https://example.com' 'https://example.com/wordpress'
# Move between subdirectories
wp search-replace 'https://example.com/old' 'https://example.com/new'Content Text Replacement
# Replace company name in all content
wp search-replace 'Old Company LLC' 'New Company Inc' wp_posts
# Fix common typos across site
wp search-replace 'recieve' 'receive' wp_posts wp_postmeta
# Update email addresses
wp search-replace 'contact@oldcompany.com' 'contact@newcompany.com'
# Replace shortcodes
wp search-replace '[old_shortcode]' '[new_shortcode]' wp_postsAdvanced Search-Replace Techniques
Handle complex scenarios with precision.
Regex Pattern Replacement
# Enable regex mode
wp search-replace 'pattern' 'replacement' --regex
# Replace all HTTP/HTTPS variations
wp search-replace 'https?://oldsite\.com' 'https://newsite.com' --regex
# Remove URL parameters
wp search-replace '\?utm_[^\s"]+' '' --regex wp_posts
# Fix malformed URLs
wp search-replace 'http://http://' 'http://' --regexWarning: Test regex patterns thoroughly with --dry-run. Incorrect patterns cause unexpected replacements.
Table-Specific Operations
# Replace in posts and metadata only
wp search-replace 'old' 'new' wp_posts wp_postmeta
# Replace in all tables except options
wp search-replace 'old' 'new' --all-tables-with-prefix --skip-tables=wp_options
# List all tables first
wp db tables
# Replace in custom tables
wp search-replace 'old' 'new' wp_custom_tableExport Replacement Report
# Generate CSV report of all changes
wp search-replace 'old' 'new' --export=changes.csv
# Generate report with dry-run
wp search-replace 'old' 'new' --dry-run --report > replacement-report.txt
# Count replacements
REPLACEMENTS=$(wp search-replace 'old' 'new' --dry-run --format=count)
echo "Will replace $REPLACEMENTS occurrences"Learn about WordPress database tables structure.
Skip Sensitive Columns
# Don't modify GUIDs (recommended)
wp search-replace 'old' 'new' --skip-columns=guid
# Skip multiple columns
wp search-replace 'old' 'new' --skip-columns=guid,post_date
# Skip user passwords and meta
wp search-replace 'old' 'new' wp_users --skip-columns=user_passBest Practice: Always skip guid column—changing GUIDs breaks feed readers and external references.
Safe Migration Workflow
Complete migration process with validation and rollback capability.
Complete Migration Script
#!/bin/bash
set -euo pipefail
# Configuration
SOURCE_URL="https://staging.example.com"
TARGET_URL="https://example.com"
BACKUP_DIR="/backups"
LOG_FILE="/var/log/migration.log"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $@" | tee -a "$LOG_FILE"
}
log "=== WordPress Migration Started ==="
# Validate WordPress
if ! wp core is-installed; then
log "ERROR: WordPress not installed"
exit 1
fi
# Create backup
log "Creating backup..."
BACKUP_FILE="$BACKUP_DIR/pre-migration-$(date +%Y%m%d-%H%M%S).sql.gz"
wp db export "$BACKUP_FILE"
BACKUP_SIZE=$(stat -c%s "$BACKUP_FILE")
if [ "$BACKUP_SIZE" -lt 1000 ]; then
log "ERROR: Backup too small, aborting"
exit 1
fi
log "✓ Backup created: $BACKUP_FILE"
# Dry-run validation
log "Validating replacements..."
if ! wp search-replace "$SOURCE_URL" "$TARGET_URL" --dry-run --report &> /dev/null; then
log "ERROR: Dry-run failed"
exit 1
fi
# Count expected replacements
REPLACEMENTS=$(wp search-replace "$SOURCE_URL" "$TARGET_URL" --dry-run --report | grep -c "$SOURCE_URL" || true)
log "Found $REPLACEMENTS occurrences to replace"
if [ "$REPLACEMENTS" -eq 0 ]; then
log "WARNING: No replacements found, check URLs"
fi
# Execute replacement
log "Executing search-replace..."
wp search-replace "$SOURCE_URL" "$TARGET_URL" --report
# Also replace without protocol
wp search-replace "//staging.example.com" "//example.com"
# Update options directly
log "Updating WordPress options..."
wp option update home "$TARGET_URL"
wp option update siteurl "$TARGET_URL"
# Verify replacements
log "Verifying replacements..."
REMAINING=$(wp search-replace "$SOURCE_URL" "$TARGET_URL" --dry-run --report | grep -c "$SOURCE_URL" || true)
if [ "$REMAINING" -gt 0 ]; then
log "WARNING: $REMAINING occurrences still remain"
else
log "✓ All occurrences replaced"
fi
# Clear caches
wp cache flush
wp rewrite flush
log "=== Migration Complete ==="
log "Rollback backup: $BACKUP_FILE"Rollback Procedure
#!/bin/bash
# rollback-migration.sh
BACKUP_FILE="$1"
if [ -z "$BACKUP_FILE" ]; then
echo "Usage: $0 <backup-file.sql.gz>"
exit 1
fi
if [ ! -f "$BACKUP_FILE" ]; then
echo "ERROR: Backup file not found: $BACKUP_FILE"
exit 1
fi
echo "Rolling back to: $BACKUP_FILE"
read -p "This will overwrite current database. Continue? (y/n) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Rollback cancelled"
exit 0
fi
# Restore database
wp db import "$BACKUP_FILE"
# Flush caches
wp cache flush
wp rewrite flush
echo "✓ Rollback complete"Troubleshooting Search-Replace
Fix common search-replace issues.
URLs Not Changing
# Check current URLs in database
wp search-replace 'oldsite.com' 'oldsite.com' --dry-run --report | head -20
# Try all URL variations
wp search-replace 'http://oldsite.com' 'https://newsite.com'
wp search-replace 'https://oldsite.com' 'https://newsite.com'
wp search-replace 'http://www.oldsite.com' 'https://newsite.com'
wp search-replace '//oldsite.com' '//newsite.com'
# Force update options
wp option update home 'https://newsite.com'
wp option update siteurl 'https://newsite.com'Serialization Errors
# Check for serialization issues
wp search-replace 'old' 'new' --precise
# Skip problematic tables
wp search-replace 'old' 'new' --skip-tables=wp_options
# Replace table by table
for TABLE in $(wp db tables --format=csv); do
echo "Processing: $TABLE"
wp search-replace 'old' 'new' "$TABLE" --dry-run
donePerformance Issues
# Large databases may timeout, increase PHP limits
php -d memory_limit=512M wp search-replace 'old' 'new'
# Process tables individually for very large databases
wp search-replace 'old' 'new' wp_posts
wp search-replace 'old' 'new' wp_postmeta
wp search-replace 'old' 'new' wp_optionsNext Steps
You now have professional WordPress search-replace skills for safe database migrations.
Recommended Learning Path
Week 1: Basic replacements
- Practice dry-run workflows
- Test domain changes on staging
- Learn serialization concepts
Week 2: Advanced techniques
- Master regex patterns
- Handle complex migrations
- Build verification scripts
Week 3: Production migrations
- Create complete migration workflows
- Implement rollback procedures
- Document migration playbooks
Week 4: Automation
- Build migration scripts
- Add validation checks
- Automate testing
Advanced Topics
- Multisite Search-Replace – Network-wide replacements
- Large Database Migrations – Handle millions of records
- Custom Table Migrations – Plugin-specific data
Get More Resources
Download migration scripts including:
- Complete migration automation
- Rollback procedures
- Validation checklists
- Weekly WP-CLI tutorials
- Migration best practices
- Advanced database techniques
Conclusion
WP-CLI search-replace is the only safe way to modify WordPress database content, handling serialized data automatically while giving you complete control and visibility.
What we covered:
✅ Search-replace fundamentals with dry-run validation
✅ Common use cases (domain changes, HTTPS migration, paths)
✅ Advanced techniques (regex, table-specific, reporting)
✅ Safe migration workflows with backups and rollback
✅ Troubleshooting common search-replace issues
Master these techniques, and you’ll handle WordPress migrations and database modifications with confidence—whether changing domains, updating URLs, or migrating content.
Ready for more? Learn WordPress database optimization or advanced WP-CLI automation.
Questions about WP-CLI search-replace? Drop a comment below!
Found this helpful? Share with other WordPress developers.
