Moving WordPress to a new host is safest when the cutover is handled as a controlled handoff instead of a last-minute copy. A clean migration keeps posts, uploads, plugins, themes, and user data aligned while preventing new comments, orders, or edits from landing on the old server after the final snapshot begins.
A working migration has two parts that must stay in sync: the database and the document root that contains /wp-config.php/, plugins, themes, uploads, and any custom drop-ins. When the public site URL stays the same, the normal path is to copy both parts to the new host, update the target database settings in /wp-config.php/, and verify that home and siteurl still point at the expected public address.
Examples assume shell access on both hosts, a working WP-CLI installation, and enough space to stage a final export outside the web root. Lower the DNS TTL before the cutover window, keep the old host available until the new host is verified, and treat URL rewriting as a separate conditional step that is only required when the public domain or protocol changes. If front-end URLs start returning rewrite-related 404 errors after the move, refresh the permalink rules on the target host before opening traffic.
Related: How to back up a WordPress site
Related: How to restore a WordPress site
Related: How to update WordPress URLs from HTTP to HTTPS
$ cd /var/www/html $ wp option get home http://www.example.com $ wp option get siteurl http://www.example.com
Run the remaining wp commands from the directory that contains /wp-config.php/. On subdirectory installs, home and siteurl can differ by design.
Make the TTL change far enough in advance for the shorter cache lifetime to take effect before the final switch. Keeping the old TTL right up to cutover can leave visitors pinned to the old host after the new site is already live.
$ wp maintenance-mode activate Enabling Maintenance mode... Success: Activated Maintenance mode. $ wp maintenance-mode status Maintenance mode is active.
Keep the old host read-only from this point forward. Do not disable maintenance mode on the source site unless the migration is intentionally rolled back.
$ BACKUP_DIR=~/backups/wordpress/migration-$(date +%F-%H%M%S) $ mkdir -p "$BACKUP_DIR" $ wp db export "$BACKUP_DIR/site.sql" Success: Exported to '/home/user/backups/wordpress/migration-2026-03-29-101530/site.sql'.
The SQL dump is the fastest rollback path if the target import or a later URL rewrite fails.
Related: How to back up a WordPress site
$ tar -czf "$BACKUP_DIR/site-files.tar.gz" --exclude='.maintenance' -C /var/www/html . $ sha256sum "$BACKUP_DIR/site.sql" "$BACKUP_DIR/site-files.tar.gz" > "$BACKUP_DIR/SHA256SUMS"
Excluding .maintenance prevents the target host from booting into maintenance mode after extraction. Recording checksums makes it easier to prove that a later transfer did not truncate either artifact.
$ rsync -avP "$BACKUP_DIR/" user@new-host:/srv/migration/wordpress/
If the bundle crosses a slow or unreliable link, verify the transferred files against the copied SHA256SUMS file before continuing.
$ mysql -u dbadmin -p -e "CREATE DATABASE wp_target CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
Skip this step when the host panel or DBA has already created an empty database and assigned the correct user.
$ sudo tar -xzf /srv/migration/wordpress/site-files.tar.gz -C /var/www/html $ sudo rm -f /var/www/html/.maintenance
If the target document root already contains an older site tree, move it aside first instead of extracting on top of it. Overlay restores are a common source of stale plugins, old uploads, and mixed-theme files.
define( 'DB_NAME', 'wp_target' ); define( 'DB_USER', 'wpuser' ); define( 'DB_PASSWORD', 'strong-password' ); define( 'DB_HOST', '127.0.0.1:3306' );
Keep the existing table_prefix unless the migration intentionally targets a different table set.
$ wp db import /srv/migration/wordpress/site.sql Success: Imported from '/srv/migration/wordpress/site.sql'.
Importing into the wrong database overwrites data quickly. Re-check the live /wp-config.php/ values before running the command.
$ wp option get home http://www.example.com $ wp option get siteurl http://www.example.com
When only the host changes, these values usually stay the same. If the target host returns an unexpected URL, fix that mismatch before browser testing or DNS changes.
$ wp db check wp_target.wp_commentmeta OK wp_target.wp_comments OK wp_target.wp_links OK wp_target.wp_options OK ##### snipped ##### wp_target.wp_users OK Success: Database checked. $ wp core verify-checksums Success: WordPress installation verifies against checksums.
Some managed or containerized environments add known root files or enforce client-side database TLS rules. If wp core verify-checksums warns only about a known extra platform file, re-run it with --exclude=<file> for that file. If wp db check fails before table output because of client SSL handling, validate the database with the host's supported MySQL or MariaDB client flags instead of assuming the import is corrupt.
$ wp db export /srv/migration/wordpress/target-pre-url-change.sql Success: Exported to '/srv/migration/wordpress/target-pre-url-change.sql'. $ wp search-replace 'http://www.example.com' 'https://www.example.com' --all-tables-with-prefix --skip-columns=guid --dry-run Table Column Replacements Type wp_options option_value 2 PHP wp_posts post_content 2 SQL wp_users user_url 1 SQL Success: 5 replacements to be made.
Skip this step when the public URL stays the same. If /wp-config.php/ defines WP_HOME or WP_SITEURL, update those constants first because they override the database values.
$ wp search-replace 'http://www.example.com' 'https://www.example.com' --all-tables-with-prefix --skip-columns=guid Table Column Replacements Type wp_options option_value 2 PHP wp_posts post_content 2 SQL wp_users user_url 1 SQL Success: Made 5 replacements. $ wp cache flush Success: The cache was flushed.
Avoid raw SQL string replacement for this job. Serialized option and metadata values can break when string lengths are not recalculated.
Use a hosts-file override, a provider preview URL, or another temporary route that sends the real hostname to the new server. Validate logins, uploads, forms, cron-triggered workflows, and any cache or proxy layer that sits in front of WordPress.
$ wp rewrite flush Success: Rewrite rules flushed.
On Apache, saving Settings → Permalinks or running wp rewrite flush --hard also regenerates the rewrite file when needed. On Nginx, confirm the server block already matches the expected WordPress rewrite rules.
If the cutover fails before the new host accepts writes, point DNS back to the old host and deactivate maintenance mode there only after confirming that no newer content was created on the target.