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.

Steps to migrate a WordPress site to a new host:

  1. Open a terminal session in the source WordPress document root and confirm the current public URLs before starting the migration.
    $ 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.

  2. Lower the DNS TTL for the live record before the migration window so the final cutover propagates faster.

    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.

  3. Enable maintenance mode on the source host immediately before the final export to stop new writes landing on the wrong server.
    $ 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.

  4. Create a timestamped migration bundle outside the web root and export the source database into it.
    $ 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.

  5. Archive the matching source files into the same bundle directory while excluding the maintenance marker.
    $ 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.

  6. Transfer the migration bundle to the new host and keep the SQL dump, file archive, and checksum file together.
    $ 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.

  7. Create an empty target database when the new host does not already have one provisioned for the migrated site.
    $ 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.

  8. Extract the file archive into the target document root and remove any copied maintenance marker before starting validation.
    $ 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.

  9. Update /wp-config.php/ on the new host so the migrated files point at the target database.
    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.

  10. Import the source database only after the target database settings are correct.
    $ 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.

  11. Verify that the migrated host still reports the expected public URLs before changing DNS.
    $ 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.

  12. Check database and core integrity on the target host before opening traffic.
    $ 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.

  13. If the public URL or protocol changes as part of the move, take a target-side rollback dump and run a dry-run replacement before writing any URL changes.
    $ 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.

  14. Apply the URL rewrite only after the dry run looks correct, then flush the cache.
    $ 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.

  15. Test wp-login.php, the front page, and a few media-heavy or transactional URLs against the new host before switching DNS.

    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.

  16. Refresh permalink rules on the new host if front-end URLs return rewrite-related 404 errors after the files and database are in place.
    $ 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.

  17. Change DNS to the new host only after the validation passes, and keep the old host read-only until traffic, logs, and admin actions stay clean through propagation.

    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.