How to Change WordPress Domain: Database & URL Migration Guide
Migrating a WordPress website to a new domain name seems straightforward in theory: you copy the filesystem, import the SQL database dump into a new target schema, and update the site and home URLs. In practice, executing this operation without serialization-aware tools will corrupt your database. Because WordPress stores options, widget parameters, and builder templates as serialized PHP strings, simply replacing domain names via standard SQL commands breaks layout configurations. This guide explains why serialization failures occur and provides a developer’s blueprint for executing migrations using WP-CLI, server-level 301 redirects, and SEO validations.
Learn more about our website migration services to Astro and Next.js to design fast, modern architectures.
Applying these domain migration strategies requires a systematic approach that balances database integrity with search visibility. Here is how to execute each strategy effectively.
1. The Core Problem: PHP Serialization and String-Length Constraints
WordPress stores many configuration options, widget layouts, and theme properties inside the database table wp_options as serialized arrays or objects. A serialized PHP string explicitly defines the data type and the byte length of string values.
The Anatomy of Serialized Strings
Consider this sample serialized string storing a domain setting:
s:25:"https://old-domain.com";
In this structure:
sdeclares the variable type as a String.25indicates that the string value contains exactly 25 characters."https://old-domain.com"is the actual string value.
The Corruption Process and Class Namespace Mappings
If you run a standard SQL REPLACE query to change the domain name (for example, replacing old-domain.com with a longer domain like my-new-company-domain.com which is 27 characters long), the query updates the value but leaves the length indicator unchanged:
s:25:"https://my-new-company-domain.com";
When WordPress retrieves this row from the database and runs the PHP function maybe_unserialize(), the parser sees the s:25 indicator, counts exactly 25 characters (stopping at n), and encounters a syntax error. The parser fails, returns false, and WordPress drops the option entirely.
Additionally, if a serialized string represents an instantiated class object:
O:31:"WPPoland\Database\CustomObject":1:{...}
The parser expects to map the string definition to a declared class structure in memory. If a migration also renames namespaces or class definitions, and the original class definition is not loaded at runtime, PHP will instantiate a placeholder object of type __PHP_Incomplete_Class. This blocks method execution and causes severe application errors. Always manage migrations using serialization-aware workflows.
2. Migrating via WP-CLI: The Command-Line Standard
The official WordPress Command Line Interface (WP-CLI) provides a built-in search-replace tool that is fully serialization-aware. This is the standard method for database migrations.
Step 1: Pre-Migration Backup
Before making any database modifications, export a full SQL backup file:
# Export the database to a SQL file
wp db export backup-pre-domain-migration.sql
Step 2: Running a Dry Run Simulation
Always execute a simulation using the --dry-run flag first. This allows you to audit the planned changes and verify the tables and columns that will be updated without modifying database rows.
Using the --precise flag forces WP-CLI to process each row using a precise PHP-based search-replace parser rather than relying on MySQL SQL wildcards. This is critical for data integrity because it guarantees that serialized objects are correctly parsed and reconstructed. If you omit the --precise flag, the engine may fall back to faster, non-serialization-aware SQL replace patterns on columns it assumes do not hold serialized arrays, which can corrupt custom layouts:
# Run a dry run replacement search
wp search-replace 'https://old-domain.com' 'https://new-domain.com' --all-tables --precise --dry-run
This output will display the exact number of replacements scheduled for each table:
+-------------------+---------+---------+
| Table | Column | Replace |
+-------------------+---------+---------+
| wp_options | 42 | 42 |
| wp_posts | 120 | 120 |
| wp_postmeta | 75 | 75 |
| wp_comments | 8 | 8 |
+-------------------+---------+---------+
Step 3: Executing the Replaces and Performance Tuning
If the dry run statistics are correct, execute the actual replacements. To speed up the process on large databases (e.g., sites with millions of rows in wp_postmeta), use the --skip-columns flag to bypass logs and transient data:
# Execute search-and-replace, skipping log and redirect tables
wp search-replace 'https://old-domain.com' 'https://new-domain.com' --all-tables --precise --skip-columns=log_content,redirects_history
Step 4: Resolving Mixed Content Issues
If you are migrating the site from HTTP to HTTPS at the same time, execute a second run to update any hardcoded insecure references:
# Replace legacy HTTP links with secure HTTPS links
wp search-replace 'http://old-domain.com' 'https://new-domain.com' --all-tables --precise
Step 5: Post-Replacement Cache Clearing
After updating your database URLs, flush your permalinks and clear the object cache:
# Regenerate rewrite rules
wp rewrite flush
# Clear the object cache
wp cache flush
3. Dynamic wp-config.php URL Overrides
If your database holds values for the old domain but you need to load the site under a temporary domain for testing, you can override these options dynamically inside wp-config.php.
Implementation of Staging Overrides
Adding URL overrides to wp-config.php temporarily redirects site routing and resource loading. However, it does not alter the actual database rows. If you leave these overrides active permanently, the site will render, but any links embedded inside post content, page metadata, or custom plugin configurations will still point to the old domain. This creates mixed content errors and broken internal paths. Therefore, you should use these overrides only as a temporary measure to gain admin dashboard access, and always execute a complete database search-and-replace operation before launching the site:
// Hardcode site URLs temporarily to bypass database options
define( 'WP_HOME', 'https://new-domain.com' );
define( 'WP_SITEURL', 'https://new-domain.com' );
Dynamic Multi-Environment Configuration
To support local development, staging servers, and production environments without changing configurations manually, set these properties dynamically based on the active server host:
// Define site URLs dynamically based on the current hostname
if ( isset( $_SERVER['HTTP_HOST'] ) ) {
$protocol = ( isset( $_SERVER['HTTPS'] ) && 'on' === $_SERVER['HTTPS'] ) ? 'https' : 'http';
define( 'WP_HOME', $protocol . '://' . $_SERVER['HTTP_HOST'] );
define( 'WP_SITEURL', $protocol . '://' . $_SERVER['HTTP_HOST'] );
}
Note: Overriding site URLs inside wp-config.php disables the corresponding inputs on the Settings > General screen in the WordPress admin panel.
4. Custom Serialization-Aware Search-Replace Script
If you need to perform migrations programmatically inside custom PHP scripts (e.g., when building automated deployment pipelines) without access to WP-CLI, use this custom class. It recursively parses arrays and objects, executes replacements, and updates string-length indicators.
Create this script in your theme directory as scripts/class-serialization-replace.php:
<?php
declare(strict_types=1);
namespace WPPoland\Database\Migrator;
class SerializationAwareReplacer {
private string $search;
private string $replace;
public function __construct( string $search, string $replace ) {
$this->search = $search;
$this->replace = $replace;
}
/**
* Entry point to recursively search and replace strings in data.
*/
public function process( $data ) {
if ( is_string( $data ) ) {
// Check if string is serialized
if ( $this->is_serialized_string( $data ) ) {
$unserialized = unserialize( $data );
if ( false !== $unserialized ) {
return serialize( $this->process( $unserialized ) );
}
}
return str_replace( $this->search, $this->replace, $data );
}
if ( is_array( $data ) ) {
$cleaned = [];
foreach ( $data as $key => $value ) {
$cleaned[ $key ] = $this->process( $value );
}
return $cleaned;
}
if ( is_object( $data ) ) {
$cleaned = clone $data;
foreach ( get_object_vars( $data ) as $key => $value ) {
$cleaned->$key = $this->process( $value );
}
return $cleaned;
}
return $data;
}
/**
* Checks if a string is serialized.
*/
private function is_serialized_string( string $string ): bool {
$string = trim( $string );
if ( 'N;' === $string ) {
return true;
}
if ( strlen( $string ) < 4 ) {
return false;
}
if ( ':' !== $string[1] ) {
return false;
}
$last_char = substr( $string, -1 );
if ( ';' !== $last_char && '}' !== $last_char ) {
return false;
}
$token = $string[0];
switch ( $token ) {
case 's':
if ( '"' !== substr( $string, -2, 1 ) ) {
return false;
}
return true;
case 'a':
case 'O':
return (bool) preg_match( "/^{$token}:[0-9]+:/s", $string );
case 'b':
case 'i':
case 'd':
return (bool) preg_match( "/^{$token}:[0-9.E-]+;\$/", $string );
}
return false;
}
}
5. Web Server Permanent 301 Redirect Rules and Edge Redirects
To preserve search authority and redirect visitors from the old domain to the new one, configure 301 permanent redirects at the web server level.
Nginx Configuration: Dynamic Wildcard Redirects
Nginx is highly efficient at handling redirects because it processes headers in memory before spawning a PHP worker process. When configuring redirects, always preserve the request URI (using $request_uri) and query string arguments. If you redirect all requests to the homepage of the new domain, search engines will treat this as soft 404 errors, causing all accumulated page authority to be lost.
Add this server block to your Nginx site configuration file:
server {
listen 80;
listen 443 ssl;
server_name old-domain.com www.old-domain.com;
# Configure SSL parameters here...
# Perform permanent redirect preserving path and query strings
return 301 https://new-domain.com$request_uri;
}
Apache Configuration: .htaccess Directives
Add these rewrite rules to the top of the .htaccess file on your old domain’s server:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTP_HOST} ^(?:www\.)?old-domain\.com$ [NC]
RewriteRule ^(.*)$ https://new-domain.com/$1 [R=301,L]
</IfModule>
Cloudflare Workers: Edge-Level Redirects
To minimize server load and speed up redirects for global users, deploy this JavaScript script to a Cloudflare Worker on your old domain:
addEventListener('fetch', event => {
event.respondWith(handleRedirectRequest(event.request))
})
async function handleRedirectRequest(request) {
const url = new URL(request.url)
// Map old path directly to the new domain
const redirectUrl = `https://new-domain.com${url.pathname}${url.search}`
return new Response('', {
status: 301,
headers: {
'Location': redirectUrl,
'Cache-Control': 'public, max-age=31536000'
}
})
}
Deploying redirects to edge servers reduces latency to under 15ms globally, preventing search engine crawler timeouts and conserving server bandwidth.
6. SEO Migration Checklist and Google Search Console Setup
When changing domain names, you must notify search engines to transfer rankings and index pages under the new address.
The GSC Change of Address Tool Workflow
- Verify Ownership: Verify both the old and new domain properties inside Google Search Console.
- Configure Redirects: Ensure Nginx or Apache permanent redirects are active and mapped correctly.
- Execute Change of Address: Inside the GSC dashboard for the old domain, navigate to Settings > Change of Address and run the verification checks.
- Submit Sitemaps: Submit your new XML sitemaps to GSC immediately after the migration is verified.
7. Migration Checklist
Use this checklist to manage your WordPress domain migrations:
- Run a full SQL and filesystem backup of the old site.
- Configure the new domain host server and install SSL certificates.
- Import the SQL database into the new database server.
- Execute WP-CLI
search-replacedry run and verify statistics. - Run the final replacement commands for HTTP and HTTPS variations.
- Flush rewrite rules, clear object caches, and verify links.
- Set up web server 301 redirect rules (Nginx/Apache).
- Submit the Change of Address notification inside Google Search Console.
8. Action Plan: A 90-Day Domain Migration Strategy
Follow this timeline to plan and monitor your site’s domain migration:
- Days 1–30 (Staging & Testing): Audit your existing hosting environment and verify that the target destination server supports SSL/HTTPS configurations. Execute database dry-run simulations using WP-CLI to map all planned URL replacements, and configure local staging domains with dynamic overrides in
wp-config.phpto run layout checks. - Days 31–60 (Database Updates & Server Redirects): Export a pre-migration database backup, import the schema into your target database server, and execute actual serialization-aware search-and-replace queries. Set up web server wildcard 301 permanent redirect blocks using Nginx or Apache config parameters, and configure Cloudflare workers for edge-level redirects.
- Days 61–90 (SEO Audits & Crawl Monitoring): Verify both properties in Google Search Console and submit the Change of Address notification. Upload new XML sitemaps to verify indexing status, inspect web logs to check search bot crawl budgets, and run broken-link diagnostic sweeps to resolve redirection issues.
Need help migrating your WordPress database or configuring server-level redirect rules? Our WordPress development team can audit your database, execute secure migrations, and manage search console setups. Contact us to discuss your project requirements.





