
Most failed WordPress migrations don't fail because someone skipped a step. They fail because the steps ran in the wrong order. Copy the database before you freeze content, and you ship a stale copy. Cut DNS before you verify, and your users become your test suite. The difference between a migration that's invisible to visitors and one that generates a week of support tickets is almost entirely sequencing: doing the right thing at the only moment it's safe to do it.
This article walks the migration as an ordered chain where each link depends on the one before it, and explains what specifically breaks when you reorder it.
A WordPress site is not a static folder. It's a filesystem and a database that drift apart continuously: every comment, order, form entry, and cache write changes the database while the files sit still. The moment you export the database, you've taken a photograph of a moving target. Everything that happens on the live site after that photograph is lost unless you account for it.
That single fact dictates the order of operations. You want the gap between "database snapshot" and "this server is live" to be as short as possible, and you want everything that can be done before the snapshot to already be done. So the sequence front-loads all the slow, fiddly work (provisioning, file transfer, TLS) and saves the database for last, right before cutover.
Stand up the new environment completely before you move a single byte of real data. That means choosing the PHP version (8.2 or 8.3 in 2026 — avoid being a version behind your source unless the source is on something ancient), matching extensions your plugins rely on (Imagick for media-heavy sites, Redis or the host's object cache if you use one), and setting MySQL/MariaDB to utf8mb4 collation. If your source predates utf8mb4 and used plain utf8, decide now whether to upgrade the charset, because doing it mid-cutover is how you turn emoji and accented characters into question marks.
Managed hosts (Kinsta, WP Engine, Cloudways, Rocket.net) hand you most of this preconfigured. On a bare VPS you're doing it by hand — which is fine, but it belongs here, days ahead, not on cutover night.
The codebase — themes, plugins, uploads — changes rarely. So move it early and move it in bulk. For anything over a few hundred megabytes, rsync over SSH beats a migration plugin because it's resumable and you can run it again later to sync only what changed:
rsync -avz --progress wp-content/ user@newhost:/path/wp-content/ gets the bulk across.Plugins like Duplicator Pro, All-in-One WP Migration, or WP Migrate are genuinely fine for smaller sites and abstract this away. But understand what they're doing: bundling files and a database dump together. The convenience hides the staleness problem rather than solving it, which is why for active sites you separate the two and time the database deliberately.
This is the link people skip, and it produces the ugliest symptom: a browser security warning on a live site. Most automatic certificate issuance (Let's Encrypt via the host's panel) validates by HTTP, which needs the domain pointing at the server — a chicken-and-egg problem at cutover. Solve it ahead of time with DNS-01 validation if your host supports it, or have the certificate pre-staged so it issues the instant DNS resolves. Either way, confirm HTTPS responds on the new IP before you touch any nameserver record.
Now, and only now, you minimize the drift window. For a content site, "freezing" can be informal — schedule cutover for a low-traffic hour and avoid publishing. For a store or membership site where orders and signups can't be lost, put the site into a brief maintenance mode so no new database writes land between snapshot and cutover.
Export with wp db export (WP-CLI) or mysqldump. Sanity-check the dump size before trusting it — a 200 MB database that exports as a 40 KB file means the dump failed silently, usually a permissions or timeout issue. Import to the destination, then immediately run your final rsync pass to catch any uploads added since step 2.
If the domain or scheme is changing (a staging URL going live, or HTTP to HTTPS), the database is full of the old URL — including inside PHP-serialized data, where strings are length-prefixed (s:19:"https://old.com/path"). A naive UPDATE ... REPLACE() in SQL changes the string but not the recorded length, corrupting the array so widgets, theme options, and page-builder layouts silently vanish or throw errors weeks later.
Use wp search-replace 'https://old.example' 'https://new.example' --all-tables --precise. WP-CLI walks serialized structures and rewrites the lengths correctly. Run it with --dry-run first to see the count of affected rows. If the domain genuinely isn't changing, skip this entirely — running it "just in case" only adds risk.
Here's the trick that lets you test the new server under its real domain without affecting anyone: add a line to your local machine's /etc/hosts (or C:\Windows\System32\drivers\etc\hosts) pointing the production domain at the new server's IP. Your browser now sees the new server as if DNS had already cut over; the rest of the world still sees the old one.
Walk the high-risk paths in this private preview: log into wp-admin, load a page-builder page, submit a contact form, and — critically — confirm a transactional email actually sends (a real wp_mail test, since new servers frequently have outbound mail blocked or an unconfigured SMTP plugin). Watch wp-content/debug.log as you go. Every problem you find here is one you fix while zero real users are affected. Every problem you don't find here, you'll fix live.
The hour you can't rush is DNS propagation, so prepare for it before cutover, not during. Several hours (ideally a day) ahead, drop the TTL on the A/AAAA or CNAME records to 300 seconds. When you finally change the record to the new IP, resolvers expire the old answer within five minutes instead of clinging to it for an hour. Some visitors will still hit the old server until their cached answer expires — this is normal, not a bug.
Because of that propagation tail, the old server must keep serving for at least 48 hours after cutover. The dangerous failure here is a split brain: a customer on stale DNS places an order on the old server, whose database you've stopped watching. For stores, the maintenance-mode freeze in step 4 is what prevents this — no writes to the old database after the snapshot means nothing to lose. Once analytics confirm essentially all traffic is hitting the new IP, you can decommission.
After cutover, re-run the same checks from your hosts-file preview, now against live DNS. Then handle the migration's quiet dependencies: update uptime monitors and integration webhooks pointing at the old IP, raise TTL back to a normal 3600 seconds, switch WP_DEBUG off, and resubmit your sitemap in Google Search Console to nudge a re-crawl. Confirm Core Web Vitals haven't regressed on the new infrastructure — your LCP should still land under 2.5 seconds and server TTFB under roughly 200 ms on a warm cache; a faster host is half the reason most people migrate, so verify you actually got it.
A migration isn't done when the new site loads. It's done when the new server has carried real traffic cleanly for about a week, every old-IP reference has been repointed, and the source has been retired. Anything earlier just looks finished.
Site
Tools
We do not sell your email. We do not spam.
© 2026 RevealTheme. All rights reserved.