Moving a WordPress site to a new host without downtime is mostly an ordering problem, not a copying problem. You bring the new copy up on a temporary address, test it from your own machine, and only then change the DNS record — so the old server keeps answering every visitor until the new one is already serving correctly.
I have moved sites between cPanel accounts, from shared hosting to a VPS, and off hosts I had stopped trusting. The steps below are the order I use now. None of them require a migration plugin, though using one is fine if you have no SSH access.
The short version
- Lower the DNS TTL to 300 seconds at least 24 hours before the move, so the switch takes minutes instead of a day.
- Copy files and database to the new host while the old site stays live.
- Run a real search-replace on the database for the old domain, with a dry run first.
- Test on the new server from your own machine only, using a hosts file.
- Issue SSL on the new host before the flip, and keep the old host paid for at least a week after.
Why migrations fail in the first place
The files are rarely the problem. What breaks is everything around them: absolute URLs stored in the database, a PHP version on the new server that the theme or a plugin does not like, a missing extension, a certificate that has not been issued for the domain yet, and DNS records with a one-day TTL still cached on the other side of the planet.
Two of those deserve planning. WordPress keeps the site address in the database, not in a config file, so a database copy that still says https://old-domain.com will keep sending the new server back to the old one. And visitors’ resolvers do not care that you changed the record an hour ago — they hold the old answer until the TTL you published expires.
Step 1 — Prepare both sides, the day before
Start with the DNS TTL. If it is currently 86400, every resolver that looks up your domain today will keep that answer for a day, even after you shorten it. That is why the TTL change goes first and the actual move goes later.
On the new host, before you copy anything: create the account, the database and the database user, then confirm the PHP version, the extensions your plugins need, and the MySQL or MariaDB version. Compare that to the old server, where a PHP info page or your host’s control panel will tell you what you are running now. If the old site runs PHP 8.2 and the new account defaults to 7.4, fix that before the copy rather than debugging it at midnight.
Take a full backup of the current site first — files and database — and keep it somewhere that is not either server. If you have not tested a restore before, do that now; the middle of a migration is a bad time to learn that your backup tool was skipping the uploads folder. I wrote the whole routine down in WordPress backups: what to back up, how often, and how to test a restore.
While you are at it, scan the old site for injected files. Moving malware to a clean host is how people get a brand-new account suspended in the first week. The quick checks are in How to check if your WordPress site is infected in 5 minutes — five minutes of looking beats an argument with a new host’s abuse desk.
Step 2 — Copy files and database
With SSH on both servers, this is two commands. Without SSH, use your host’s file manager for the files and phpMyAdmin for the database, or a migration plugin, which does the same thing with less control.
# database, on the old server
mysqldump --single-transaction --quick --default-character-set=utf8mb4 -u olduser -p olddb > olddb.sql
# files, skipping caches
rsync -avz --exclude 'wp-content/cache' --exclude 'wp-content/uploads/cache' \
/home/olduser/public_html/ newuser@newhost:/home/newuser/public_html/
# import on the new server
mysql -u newuser -p newdb < olddb.sql
Two things people get wrong here. Do not copy the old wp-config.php over the new one — the database name, user and password are different. And do not skip the uploads folder to save time; a site that loads fine but shows broken images is not migrated, it is half-migrated.
Step 3 — Search-replace, properly
Change the site URL to the new domain in the database, not by hand in the admin. WordPress stores many URLs inside serialised PHP arrays, where the string length is written next to the value. A plain SQL UPDATE that changes old-domain.com to new-domain.com changes the text but not the length, and the row silently breaks — you find out later when a widget or theme option page turns blank.
WP-CLI handles this correctly, and it has a dry run that tells you how many rows would change before anything is written:
wp search-replace 'https://old-domain.com' 'https://new-domain.com' \
--all-tables --precise --dry-run
# same command without --dry-run when the row count looks right
Run it on the new server, after the import. Then update WP_HOME and WP_SITEURL if you define them in wp-config.php, or you will overwrite your own fix on the next page load.
Step 4 — Test before anyone can see it
The new site is now correct on the new server, and nobody knows it exists. Keep it that way. Point only your own machine at it by editing the hosts file — on Windows, C:\Windows\System32\drivers\etc\hosts, one line with the new server’s IP and the domain — then flush your DNS cache and open the site in a private window.
Click the things that actually matter, in this order: the home page, an old post permalink, the login page, a form submission, an image from the media library, and the sitemap. Then open the error log on the new server; a fresh copy with a missing extension announces itself there, not on the page. If you get a 500, this is the log to read first.
Check for mixed content too. If the old site loaded images over plain HTTP and the new one forces HTTPS, those images will vanish in the browser until the database no longer contains http:// versions of your own domain.
Step 5 — Flip the DNS, then watch
Change the A record (and the AAAA record if you had one) to the new server’s IP. Leave every other record alone — MX, SPF, DKIM and any verification TXT records belong to your email, not to the website, and moving them is how a site migration turns into a mail outage.
Propagation is not instant and it is not uniform. Check from more than one network: a command line lookup, a public DNS checker, and a phone on mobile data. Once those agree, do the same click-through you did on the hosts file, this time against the real domain.
Keep the old hosting account active for at least a week. It costs you one billing cycle and it is the only rollback you have that works in minutes — point the A record back and you are live on the old copy again. Cancel it after the new site has run through a full day of normal traffic and a scheduled backup.
The 48-hour checklist
| When | Do this | Done when |
|---|---|---|
| T-48h | Lower TTL to 300, take a full backup, scan for injected files | Old TTL has expired everywhere |
| T-24h | Create account and database, match PHP version, request SSL | Certificate issued for the domain |
| Move day | Copy files, import database, search-replace, fix wp-config | Site loads on the hosts-file test |
| Flip | Change the A record only, leave MX and TXT alone | Two networks resolve to the new IP |
| T+2h | Re-test login, forms, images, sitemap, error log | No new errors since the flip |
| T+72h | Confirm cron ran, backup ran, mail still delivers | Old host can be cancelled |
What to do right now
Go and lower the TTL on the domain you care about, today, even if you are not moving this month. It is one field in a control panel, it costs nothing, and it turns a future migration from an overnight gamble into a twenty-minute change. Everything else on this page only works if that one is already done.

Leave a Reply