Migrating WordPress Without Downtime
Every botched WordPress migration follows the same script: someone copies files and a database on Saturday night, flips DNS, and spends Sunday discovering what broke — missing images, mixed-content warnings, a contact form silently failing, orders from the transition window lost in the void. The site was "down" for hours, not because copying a site is hard, but because the sequence was wrong.
The truth worth internalizing: zero-downtime migration is a sequencing problem. The technical steps are individually easy. What separates a clean cutover from a lost weekend is doing them in the right order, with a testing step most guides skip and a rollback path most people never prepare. This is the order of operations, as a field guide.
Phase 0: Three days before — the DNS groundwork
DNS is the step everyone thinks about last and should think about first, because it's the only step with a mandatory waiting period.
Every DNS record carries a TTL (time to live) — how long resolvers worldwide may cache the answer before asking again. If your domain's A record has a TTL of 24 hours (common as a default), then some visitors will keep hitting your old server for up to a day after you change the record. You cannot speed this up on cutover day; the caches out in the world already hold the old answer with the old expiry.
So the very first migration action, days before anything else:
- Check the current TTL on the A/AAAA (and any CNAME) records for the domain — your DNS host's control panel shows it, or
dig yourdomain.comshows the remaining TTL. - Lower it to 300 seconds (5 minutes).
- Wait out one full old-TTL period before cutover day. If the old TTL was 24 hours, lower it at least 24 hours in advance — ideally 48–72 for margin.
Do this and your eventual DNS change propagates to effectively everyone within minutes. Skip it and no amount of skill later prevents a long, smeared transition where traffic hits both servers for a day.
While you're in this phase, also take inventory: PHP version on old and new hosts (mismatched major versions are a top source of post-migration white screens), any server-level config living outside WordPress (nginx redirects, .htaccess rules, cron entries, environment variables), external services pinned to your old server's IP (some payment and API providers whitelist IPs), and email — if your mail flows through the old host, migrating the website without a mail plan takes your inbox down, which is worse than the website blipping.
Phase 1: Copy everything to the new host
Move the site's two components — files and database — by whatever means suits the size:
- Small sites (under ~1GB): a migration plugin that packages everything is fine and handles URL rewriting for you.
- Large sites: rsync (or SFTP) for files,
mysqldumpor WP-CLI for the database. Plugins that try to zip a 20GB uploads folder through PHP tend to die at exactly the wrong moment. - Either way: exclude junk before it travels — old backup archives inside wp-content, cache directories, log files. Migrations are a great excuse to not carry the attic with you.
Two technical details deserve care here:
URL handling in the database
Your site URL is embedded throughout the database — not just in the two options rows, but inside post content, widget settings, and (the part that bites people) serialized PHP data, where a naive find-and-replace corrupts the string-length markers and breaks settings in ways that surface days later. Always use a serialization-aware tool: WP-CLI's wp search-replace or a migration plugin that advertises serialized-data support. Never raw SQL REPLACE().
Keep the same domain during the copy
If your new host lets you set up the site under your real domain name (rather than a temporary URL), do it — you'll skip URL rewriting entirely, and the hosts-file trick in the next phase makes testing possible without DNS. If you must use a temporary URL first, you'll do the search-replace dance twice; it works, it's just two more chances for a mistake.
Phase 2: Test the new site before the world sees it — the hosts-file trick
This is the step that separates professionals from gamblers, and it costs five minutes. Your computer consults a local file — /etc/hosts on Mac/Linux, C:\Windows\System32\drivers\etc\hosts on Windows — before asking DNS. Add a line pointing your domain at the new server's IP:
203.0.113.42 yourdomain.com www.yourdomain.com
Now your machine, and only your machine, sees the new server when browsing the real domain. The rest of the world still gets the old site. You can test the complete new setup — real domain, real URLs, real SSL — with zero public exposure.
What to actually test (write this list down; adrenaline erases memory on cutover day):
- Homepage, a handful of deep pages and posts, category archives, search.
- Images loading — and specifically some recent uploads, which catch incomplete file syncs.
- HTTPS: valid certificate on the new server before cutover. Most hosts can issue a Let's Encrypt cert pre-cutover via a DNS challenge or by temporarily serving the validation; ask your new host how they handle this, because an HTTPS site cut over to a server without a certificate is very publicly broken.
- Log in to wp-admin. Update a draft. Upload a file.
- Submit every form. Confirm the email actually arrives.
- If WooCommerce: complete a test purchase in sandbox mode, check that webhooks (Stripe, PayPal) can reach the new server.
- Check the new server's PHP error log while you click around. Silence is the goal.
Remember to remove the hosts-file line when done testing, and again after cutover — a forgotten hosts entry has confused many a developer a month later.
Phase 3: The content freeze and final sync
Here's the problem the naive migration ignores: your site kept changing while you copied it. Comments, orders, form entries, and edits made after your database export exist only on the old server and will be silently lost at cutover.
The solution is a content freeze — a short window where nothing changes on the old site — combined with a final delta sync. The sequence:
- Announce the freeze to anyone who edits the site. For a blog, stop publishing for an hour. For WooCommerce, either pick your quietest traffic hour (analytics will tell you; often early morning local time) or briefly enable a "checkout paused, back in 30 minutes" notice — a transparent 20-minute pause beats losing an order.
- Re-run the file sync. rsync makes this fast — it transfers only what changed since Phase 1.
- Re-export and re-import the database (or, for large sites, just the tables that change: posts, comments, orders, users). This final dump is small and quick precisely because you did the heavy copy earlier.
- Quick re-test through the hosts file: latest posts present, latest orders present.
The freeze window for a well-prepared migration is 15–45 minutes — that's the window during which the old site must stay static, not downtime. Visitors see a fully functional site throughout.
Phase 4: Cutover
The anticlimax, if you've done everything above:
- Change the A record (and www CNAME) to the new server's IP.
- Because your TTL is 300 seconds, traffic shifts to the new server over the next several minutes.
- Watch the new server's access log to see real traffic arriving; watch the old server's log to see it drain.
- End the content freeze once the new server is receiving the traffic.
During the shift, both servers serve identical content — which is exactly why visitors experience nothing. That's the whole trick of zero-downtime migration: make the transition window boring by making both sides of it identical.
Keep the old server running and paid for at least a week. It is your rollback: if something catastrophic surfaces on day two, pointing DNS back is a five-minute operation — but only if the old server still exists. Decommissioning the old host on cutover day to save a week's fee is the single most regretted economy in this entire process.
Phase 5: The first 48 hours — what actually breaks
A field guide should tell you where the bodies are buried. Post-migration issues cluster predictably:
- Email deliverability. The new server's IP isn't in your SPF record, so WordPress-sent mail (receipts, form notifications, password resets) starts landing in spam or bouncing. Fix: add the new host to SPF, or better, route WordPress email through a transactional provider so it never depends on the web server at all.
- Cron-based tasks stall. If the old host ran a real cron job hitting wp-cron, recreate it. Scheduled posts stuck in "missed schedule" is the classic symptom.
- Stragglers hitting the old server. A few corporate and ISP resolvers ignore TTLs. This is why the old server stays up serving identical content, not a maintenance page.
- Hardcoded paths. Anything referencing the old server's filesystem path (
/home/olduser/public_html/...) — some plugins cache absolute paths in the database. Symptoms: broken image processing, failed uploads. A search-replace on the old path fixes it. - Search Console and monitoring. Nothing to change for SEO if URLs didn't change — Google doesn't care which server answers — but re-check your uptime monitoring points at the right expectations, and watch Search Console's crawl stats for a few days for anomalies.
The one-page version
| When | Action |
|---|---|
| T minus 3 days | Lower DNS TTL to 300s. Inventory PHP versions, email, crons, IP whitelists. |
| T minus 1–2 days | Full copy of files + DB to new host. Serialization-safe URL handling. Pre-issue SSL. |
| T minus 1 day | Test everything via hosts file. Fix what's broken. Re-test. |
| Cutover hour | Content freeze → delta sync (files + DB) → verify → flip DNS → watch logs → unfreeze. |
| T plus 1 week | Old server stays alive as rollback. Fix SPF, crons. Then decommission and restore TTL. |
None of this is glamorous, and that's the point. A migration that goes well is one where the most exciting moment is watching access-log lines appear on the new server. Prepare the DNS early, test through the hosts file, freeze briefly, sync the delta, and keep the old server breathing until the new one has earned your trust. The weekend stays yours.
Photo: panumas nikhomkhai on Pexels