How to Migrate a WordPress Site (Without Breaking It)

WordPress migration done safely: pre-migration checklist, manual method, Duplicator plugin, and what to do after migration to avoid SEO damage.

Dobromir Dechev
Dobromir WordPress agency owner

Migrating a WordPress site is one of those tasks that looks straightforward until you are three hours in, staring at a white screen of death on the new host, with the client calling to ask why their site is down. The actual file and database transfer is simple. What breaks migrations is the stuff around it — serialised data in the database that a naive find-and-replace destroys, DNS changes made before the new site is confirmed working, PHP version mismatches that only surface after go-live, and SMTP settings tied to the old host that silently stop sending email. This guide covers every method from manual to fully automated, plus the pre- and post-migration steps that prevent the common disasters.


Pre-migration checklist

Do not start moving files until you have checked every item on this list. Skipping any of these is the most common reason migrations turn into incidents.

  • Full backup of files and database — Take a fresh backup immediately before starting. Not last week's automated backup — a new one, right now. Files and database. Store it somewhere outside the current host (local drive, S3, Google Drive).
  • Note the PHP version on the current host — Go to phpinfo or check your hosting panel. Your new host needs to match or exceed this version. Upgrading PHP mid-migration (e.g., 7.4 to 8.2) risks plugin compatibility issues that look like migration failures but are actually PHP deprecation errors.
  • Check plugin compatibility with the new host — Certain plugins behave differently on different server stacks. Security plugins (Wordfence, iThemes Security) may block the migration process itself. Caching plugins (WP Rocket, W3 Total Cache) need to be deactivated before the move to avoid stale cache state on the new host.
  • Lower DNS TTL to 300 seconds at least 24 hours before — Log into your DNS provider now and drop the TTL on your A record to 300 (5 minutes). This means when you update the nameservers or A record after migration, the change propagates in 5 minutes instead of 24 hours. If you miss this step you are waiting hours after migration to verify the live site.
  • Get a temporary URL on the new host — Most hosts give you a way to access the new server before DNS is pointed: a staging subdomain, a temporary URL (server123.newhost.com/~username), or a hosts file trick. Set this up before you start so you can test the migrated site without touching DNS.
  • Disable all caching on both old and new site during migration — Cached pages, transients, and object cache data will not migrate cleanly. Clear everything, deactivate caching plugins, and do not re-enable until the migration is confirmed working.

Method 1 — Manual migration

The manual method is the most reliable for complex sites, large databases, or situations where you need full control at every step. It requires SSH access (preferred) or FTP.

Step 1 — Export the database

With WP-CLI (recommended):

wp db export backup-$(date +%Y%m%d).sql --add-drop-table

The --add-drop-table flag adds DROP TABLE IF EXISTS before each CREATE TABLE, which prevents errors if you import into a database that already has tables.

With phpMyAdmin:

Log in, select the database, click Export, choose Custom, select all tables, check "Add DROP TABLE", choose SQL format, export. For databases over 50MB, phpMyAdmin often times out — use WP-CLI or mysqldump instead:

mysqldump -u dbuser -p dbname > backup-$(date +%Y%m%d).sql

Step 2 — Download all site files

With rsync (fastest, preserves permissions):

rsync -avz --progress [email protected]:/var/www/html/ ./site-backup/

With FTP:

Connect to the old host, navigate to the site root (the folder containing wp-config.php), and download everything. This includes wp-content/, wp-admin/, wp-includes/, and all root-level files. Do not skip hidden files like .htaccess.

Step 3 — Create database on new host

Log into the new host's control panel (cPanel, Plesk, or your VPS panel) and create:

  1. A new MySQL database
  2. A new database user with a strong password
  3. Grant all privileges to that user on that database

Note down the database name, username, password, and hostname (usually localhost).

Step 4 — Import the database

With WP-CLI on the new host:

wp db import backup-20260328.sql

With phpMyAdmin on the new host:

Select the new empty database, click Import, upload the SQL file. For large files, use the command line instead:

mysql -u newdbuser -p newdbname < backup-20260328.sql

Step 5 — Upload files to new host

Upload everything you downloaded in Step 2 to the document root of the new host. With rsync:

rsync -avz --progress ./site-backup/ [email protected]:/var/www/html/

Step 6 — Update wp-config.php

Open wp-config.php on the new host and update the database credentials:

define( 'DB_NAME', 'new_database_name' );
define( 'DB_USER', 'new_database_user' );
define( 'DB_PASSWORD', 'new_database_password' );
define( 'DB_HOST', 'localhost' );

If the old site was at a different domain (e.g., staging.example.com moving to example.com), do not update WP_HOME or WP_SITEURL in wp-config.php yet — do the search-replace first.

Step 7 — Search-replace old domain in database

This is the step most tutorials get wrong. You cannot do a simple text search-replace on a WordPress database because WordPress stores serialised PHP arrays in the database. These look like:

a:2:{s:3:"url";s:22:"http://old.example.com";}

The s:22: is the string length. If you do a naive find-and-replace and the new URL is a different length, the serialised data becomes invalid and PHP throws errors. Use WP-CLI's search-replace, which handles serialised data correctly:

wp search-replace 'http://old.example.com' 'https://new.example.com' --all-tables --precise --report-changed-tables

The --all-tables flag catches custom table prefixes and plugin tables that live outside the standard wp_ prefix. The --precise flag uses PHP to handle serialised data rather than SQL REPLACE(), which is safer for complex data structures.

If you are migrating to a temp URL first:

wp search-replace 'http://old.example.com' 'http://temp.newhost.com' --all-tables

Then replace again when you point the real domain:

wp search-replace 'http://temp.newhost.com' 'https://real.example.com' --all-tables

Step 8 — Test on temporary URL, then update nameservers

Access the site using the temporary URL or hosts file trick. Test everything: homepage, inner pages, admin login, contact forms, WooCommerce checkout if applicable, image loading. Only when everything works do you update the nameservers or A record.

Update your A record to the new server IP. Because you lowered the TTL in the pre-migration checklist, propagation should complete within 5-10 minutes.


Method 2 — Duplicator plugin

Duplicator creates a self-contained package — an installer.php script and an archive.zip — that you upload to the new host and run through a browser-based installer. Good for sites where you do not have SSH access on the new host.

Creating the package on the old site:

  1. Install and activate Duplicator on the old site (free version on WordPress.org)
  2. Go to Duplicator > Packages > Create New
  3. Run the pre-installation scanner — Duplicator checks for large files, server limits, and potential problems. Fix any warnings before proceeding
  4. Build the package. For large sites (over 500MB), this can take several minutes
  5. Download both installer.php and the archive.zip file — you need both

Installing on the new host:

  1. Create the new database on the new host (same as Method 1, Step 3)
  2. Upload both installer.php and archive.zip to the document root of the new host
  3. Navigate to http://newhost.com/installer.php in your browser
  4. Work through the installer wizard: enter the new database credentials, confirm the new domain URL, run the install

If installer.php fails:

  • Memory limit error — Add define('WP_MEMORY_LIMIT', '256M'); to wp-config.php, or increase PHP memory limit in php.ini / .htaccess (php_value memory_limit 256M)
  • File permission error — The document root needs to be writable. SSH in and run chmod 755 /var/www/html/
  • Archive extraction timeout — Large archives can exceed PHP's max execution time. Increase it: php_value max_execution_time 300

Cleanup after successful install:

Duplicator leaves installer.php, installer-backup/, and dup-installer/ in the root after a successful migration. Remove them immediately — installer.php in particular can be a security risk as it exposes your database credentials to anyone who visits the URL:

rm installer.php
rm -rf installer-backup/ dup-installer/

Duplicator also reminds you to do this in the post-install screen, but clients often skip it.


Method 3 — All-in-One WP Migration

All-in-One WP Migration (by ServMask) is the most beginner-friendly migration tool. The process is: export from old site, import on new site. No FTP knowledge required, no database credentials to manage.

On the old site: Install All-in-One WP Migration, go to All-in-One WP Migration > Export, choose Export To > File. The plugin creates a .wpress file containing everything.

On the new site: Install All-in-One WP Migration, go to Import, drag and drop the .wpress file.

The free version has a 512MB import limit. For most brochure sites this is fine. For larger sites, the options are:

  • ServMask extensions (paid, ~$69–$99): Remove the import size limit entirely
  • Basic extension (free via the plugin's GitHub): raises the limit to what your server's PHP allows — typically enough for sites up to a few GB
  • Use Method 1 or Method 2 instead for large sites where paying for the extension is not justified

One limitation: All-in-One WP Migration does not give you control over the search-replace process. It handles the domain replacement automatically during import. If you need to migrate to a temp URL first, verify it, then switch to the real domain, WP-CLI's search-replace gives you more control.


Method 4 — Host-provided migration tools

If you are migrating to a managed WordPress host, most of them offer tooling that removes most of the complexity.

Kinsta — free migration service:

Every Kinsta plan includes at least one free migration performed by their engineering team. You submit a migration request through MyKinsta, provide FTP/SSH credentials and database access for the old host, and Kinsta's team handles the entire transfer. They use their own migration tooling, handle the search-replace, and notify you when the migrated site is ready to test on a temporary URL. For high-traffic or complex WooCommerce sites, this is the lowest-risk migration option available. Additional migrations beyond the free allowance are available for a fixed fee.

Cloudways — Cloudways Migrator plugin:

Install the free Cloudways Migrator plugin on the source (old) site, enter your Cloudways server credentials and a migration key generated from the Cloudways dashboard, and the plugin transfers files and database directly between servers over SSH. No archive file to download and re-upload. The process is incremental for large sites — it transfers files in batches to avoid timeouts. Works well for sites up to several GB.

WP Engine — automated migration plugin:

WP Engine provides a WP Migration plugin that connects to your WP Engine account and migrates directly. Similar to the Cloudways approach — install on source, authenticate, push to destination.

For all host-provided tools: test on the temporary URL they provide before updating DNS. The tools are reliable but they cannot catch configuration issues specific to your site — a plugin that hard-codes the old server IP, WooCommerce shipping zone misconfigurations, or SSL certificate issues that only surface at the real domain.


Post-migration checklist

The migration is not complete when the files land on the new server. Run through all of these before marking the job done.

  • Flush permalinks — Go to Settings > Permalinks in the WordPress admin and click Save Changes without changing anything. This regenerates the .htaccess rewrite rules for the new server. Skipping this causes all pages except the homepage to return 404s.
  • Check all images load — Browse several pages, particularly pages with galleries or featured images. Missing images usually mean a failed file transfer or a URL mismatch in the database.
  • Test all forms — Contact forms, newsletter signups, WooCommerce checkout. Submit a real test entry and verify it arrives.
  • Test checkout end-to-end (WooCommerce) — Place a real test order with a test payment method. Verify the order confirmation email is sent and received.
  • Verify SSL — If the new host uses a different SSL certificate setup, check that https:// loads cleanly with no mixed content warnings. Use curl -I https://yourdomain.com to verify the certificate is valid and the response is 200 OK.
  • Submit updated sitemap to Google Search Console — Log into Search Console, go to Sitemaps, and resubmit https://yourdomain.com/sitemap.xml. This prompts Google to re-crawl with the new server's response times and signals that the site is healthy at the new location.
  • Monitor redirects — Use a redirect checker tool (Screaming Frog, Redirect Checker, or curl -L -I) to verify that important URLs still resolve correctly and are not bouncing through multiple redirects.
  • Update SMTP settings — Many sites use SMTP credentials tied to the old host's email server or a dedicated service (SendGrid, Mailgun). Check the SMTP plugin settings and send a test email from Settings > General or your SMTP plugin's test function.
  • Remove Duplicator installer files — If you used Duplicator, delete installer.php and the installer-backup/ directory from the server root immediately.
  • Cancel or redirect the old host — Do not cancel the old hosting account immediately. Keep it for at least 2–4 weeks as a fallback. Put the old site in maintenance mode or redirect all traffic to the new domain. After 4 weeks with no issues, cancel.

Common migration mistakes

These are the errors that show up again and again on client sites:

Not doing search-replace correctly on serialised data

Using MySQL's REPLACE() function directly, or a text editor's find-and-replace on the exported SQL file, corrupts serialised data. Theme options, widget configurations, plugin settings — all of these are stored as serialised PHP. Always use WP-CLI's wp search-replace or a tool specifically designed for WordPress databases.

Forgetting to update wp-config.php credentials

The site files transferred successfully but the database credentials in wp-config.php still point to the old host's database. The result is "Error establishing a database connection" on the new site. Always update DB_NAME, DB_USER, DB_PASSWORD, and DB_HOST immediately after uploading files.

Leaving the old site live after DNS switch

After DNS propagates, both the old and new sites may be accessible simultaneously for a period, depending on DNS cache state at different locations. If the old site is still serving content, visitors in different locations see different versions of the site, form submissions may go to the old database, and Google may index duplicate content. Put the old site into maintenance mode or redirect to the new domain as soon as DNS is switched.

Ignoring email deliverability after migration

WordPress sends email via the server's local mail function by default. The new server may not be configured for outbound email, may not have the domain in its SPF record, or may be on a shared IP with a poor sending reputation. The result is contact forms that appear to submit successfully but the emails never arrive. Set up a transactional email service (Postmark, SendGrid, Mailgun) with an SMTP plugin as part of every migration.

Not testing on the temp URL before updating DNS

Switching DNS and then discovering a problem means rolling back DNS (which takes more time), working on a live-but-broken site that visitors can see, and handling an angry client. Always verify the migrated site works completely on the temporary URL before touching DNS.


Was this article helpful?