Blog · Guides

Migrating WordPress Without Downtime or Data Loss: A Step-by-Step Runbook

Migrating WordPress goes wrong in a handful of predictable ways: DNS propagation catches you mid-cutover, a raw SQL find-replace mangles serialized data, or nobody checks the new environment until visitors already start hitting it. None of that is inevitable. A migration is just a sequence of steps done in the right order, with verification built in at each stage. Here is the runbook I use.

Before you touch anything: audit and prepare

Do this a few days before the actual move, not the night before.

  • Confirm the target environment matches or exceeds the source. Same or newer PHP version (PHP 8.3 with OPcache enabled is a solid default today), same or better MySQL/MariaDB version, and any required PHP extensions already installed.
  • Inventory plugins and themes. Note anything with hardcoded paths, license keys tied to a domain, or a dependency on a specific server module (mod_rewrite equivalents, ionCube, etc).
  • Test your backups now, not during the migration. A backup you have never restored is a hope, not a backup. Restore it to a scratch environment and confirm the site loads.
  • Lower your DNS TTL 24 to 48 hours ahead of cutover. If your current TTL is set to something like 3600 or 86400 seconds, drop it to 300 (5 minutes) now. This is the single biggest lever for a fast, low-downtime cutover later, since resolvers will pick up your change quickly instead of caching the old IP for hours.

Step 1: take a full, verified backup

You need both the database and the files. With wp-cli on the source server:

wp db export backup-$(date +%F).sql
tar -czf site-files-$(date +%F).tar.gz wp-content wp-config.php

Copy both artifacts off the source server to a third location (your laptop, object storage, whatever) before you do anything else. If the migration goes sideways, this is your undo button.

Step 2: stand up the new environment and restore

Provision the new hosting environment, upload the files, and import the database:

wp db import backup-2024-01-01.sql

Update wp-config.php on the new server with the correct database name, user, password, and host. Leave the site URL settings alone for now; you will handle those in the next step with search-replace, not by hand-editing rows.

Step 3: run a serialization-safe search-replace

This is where most DIY migrations quietly break. WordPress stores a lot of configuration (widget settings, some plugin options, theme mods) as PHP serialized arrays inside the database. A serialized string embeds the byte length of each value, like s:19:"https://oldsite.com". A naive SQL UPDATE ... REPLACE() across the whole database will change the URL text but not the length prefix, and PHP will refuse to unserialize the corrupted value. The result: missing widgets, broken theme options, silent errors that only surface later.

wp-cli’s search-replace command handles this correctly, recalculating serialized lengths as it goes. Always dry-run first:

wp search-replace 'https://oldsite.com' 'https://newsite.com' --all-tables --dry-run

Review the reported number of replacements per table. Does it look plausible for your content size? If yes, run it for real:

wp search-replace 'https://oldsite.com' 'https://newsite.com' --all-tables

If your old and new domains differ only by protocol (http to https) or by a www prefix, run a second, narrower pass for that variant too, since editors often paste full URLs into post content with whichever scheme was live at the time.

Step 4: test on the new server before touching DNS

You do not need to go live to verify the new environment. Edit your local machine’s hosts file (or use a browser plugin) to point your domain at the new server’s IP address while DNS still points at the old one. This lets you browse the real domain, hitting the new server, without affecting any live visitor.

Run through this checklist on the staged new environment:

  • Homepage, a handful of posts, and any custom page templates render correctly
  • Permalinks work (not just the homepage); run wp rewrite flush if you see 404s on inner pages
  • Forms submit and email notifications arrive
  • Checkout or membership flows complete end to end, if applicable
  • Media library images load, including ones referenced in older posts
  • Admin login works, and any 2FA setup carried over correctly
  • Scheduled cron tasks are registered (wp cron event list)
  • SSL certificate is issued and valid for the new server, or your CDN/edge layer is configured to handle it

If you use Cloudflare in front of the site, this is also the point to update the DNS record’s target IP (while keeping the proxy status and origin IP unpublished, which matters for keeping the origin harder to target directly) and to set the LiteSpeed Cache plugin’s server settings if you’re moving onto or between LiteSpeed environments.

Step 5: cut over

Once the hosts-file test passes, you are ready:

  1. Put the old site into a brief maintenance/read-only mode if it accepts new content (comments, orders) that could be lost between now and cutover.
  2. Do a final database export and import for anything created since your first backup, then rerun the search-replace pass on just the delta if needed.
  3. Update the DNS A record (or Cloudflare’s proxied record) to point at the new server. Because you lowered the TTL earlier, most resolvers will pick this up within minutes.
  4. Purge caches: wp cache flush on the new server, plus a LiteSpeed Cache purge and a Cloudflare cache purge if either sits in front of the site, so nobody sees stale assets or old-server responses.
  5. Watch traffic logs on both servers for the next hour. You’ll see requests taper off the old server as DNS propagates.

Step 6: verify after cutover

Do not consider the migration done the moment DNS switches. Check these over the following day:

  • Run Lighthouse or PageSpeed Insights against the live site and compare lab metrics to your pre-migration baseline. Remember that field data (real user Core Web Vitals like LCP, INP, and CLS) takes longer to populate than a single lab test, so don’t panic over one lab run; watch the trend over the following days.
  • Confirm search engine verification (Search Console, analytics tags) still resolves correctly.
  • Check that redirects from any old URLs still 301 properly if you changed permalink structures.
  • Re-run your hardening checklist on the new server: strong unique passwords with 2FA, login attempt limiting, XML-RPC disabled if unused, DISALLOW_FILE_EDIT set in wp-config.php, and least-privilege roles for any users who only need editor access.
  • Schedule a fresh, automated off-site backup on the new server and confirm it actually runs. A host like ServerBorn sets this up as part of the managed stack, but if you’re self-managing, don’t skip it.
  • Once you’re confident, restore DNS TTL to a normal value (3600 or higher) to reduce query load on your DNS provider.

Takeaway

The difference between a smooth migration and a stressful one is almost entirely in the order of operations: back up and verify first, stage DNS early, use wp-cli’s search-replace instead of raw SQL to protect serialized data, and test the new environment thoroughly before DNS ever changes. Do the verification checklist both before and after cutover, and treat the migration as complete only once field performance data and backups on the new server both check out.