Almost every catastrophic SEO incident we've audited started with a migration that nobody fully tested. New CMS, new domain, new URL structure, new templates — pick any one of those and the chance of breaking something is significant. Pick all four at once and you're almost guaranteed to lose a meaningful share of organic traffic unless you handle redirects perfectly.
This guide is the workflow we use for client migrations. It's not glamorous — it's mostly spreadsheets and verification — but it's the difference between a migration that goes unnoticed and one that takes six months to recover from.
The Single Most Important Idea
Every URL that has ever received traffic, has external inbound links, or is in Google's index needs to either:
- Continue to serve the same (or better) content at the same URL after migration, OR
- 301 redirect to the closest equivalent on the new site.
There's no third option. A URL that 404s post-migration loses its ranking, its inbound links, and any direct traffic. A URL that 301s to your homepage instead of a real equivalent loses most of its ranking value (Google explicitly treats homepage redirects as soft 404s).
The whole migration process flows from this principle.
Step 1: Build a Complete URL Inventory
Before you change anything, you need an authoritative list of every URL on the current site. Sources to combine:
- Existing sitemap.xml. Captures most public URLs. Don't trust it as complete — sitemaps often miss old archive content.
- CMS database export. If WordPress, every post, page, attachment, and category. If Shopify, every product, collection, page, and blog post.
- Server logs. The last 90 days of access logs reveal URLs that exist (or existed) and got real traffic. This is where you'll find old archive URLs nobody remembered.
- Google Search Console. Performance → Pages shows every URL Google has shown in search results in the last 16 months. Include them all.
- Google Analytics. Behaviour → Site Content → All Pages, sorted by pageviews. Long-tail traffic comes from URLs that aren't in your sitemap.
- External backlink data. Ahrefs, Moz, Majestic, or Search Console's Links report. URLs other people have linked to are doubly important to redirect correctly.
- The
/robots.txt+/sitemap.xmlof every CDN endpoint and subdomain. Don't forget API URLs, asset URLs, and old subdomains.
De-duplicate the combined list. You'll typically end up with two or three times as many URLs as the public sitemap suggested. That's normal.
Step 2: Map Every URL to Its New Destination
For each URL on the old site, the destination is one of:
- The same URL on the new site. Best case — no redirect needed.
- A different URL on the new site that serves equivalent or better content. 301 the old URL to the new one.
- A category, parent page, or related content. Acceptable for retired pages, but not for high-value content. 301.
- 410 Gone. Use this for URLs that genuinely don't have an equivalent and won't ever come back. Tells search engines to drop the URL from the index immediately, rather than retrying for weeks. Better than 404 for known-permanent removals.
Avoid:
- Mass-redirecting to the homepage. Soft 404 from Google's perspective. Don't.
- Pattern-based redirects without checking each result. A regex that maps
/blog/(*)/to/articles/$1/is fast but will produce wrong destinations for the edge cases (the slug that didn't survive the migration, the old post that was merged with another).
Build the map in a spreadsheet. Two columns: old_url, new_url. Save it; you'll need it again multiple times.
Step 3: Generate the Redirect Rules
Translate the spreadsheet into your server's redirect format. Most platforms support importing a CSV directly:
- WordPress with the Redirection plugin — has a CSV import that consumes
old_url, new_urldirectly. - Cloudflare Bulk Redirects — accepts a CSV of source/destination pairs.
- nginx — generate a separate config file with one
location = /old { return 301 /new; }block per row. - Apache .htaccess — generate one
Redirect 301 /old /newper row, or useRewriteMapfor huge maps.
Whatever the format, the rules should be in version control. If something breaks post-launch, you need to be able to roll back, audit, and re-deploy.
Step 4: Test Every Redirect Before Launch
This is the step that gets skipped on tight timelines and causes most of the post-launch damage. Test in a staging environment that exactly mirrors production:
- Deploy the redirect map to staging.
- Take the spreadsheet's
old_urlcolumn and run every URL through curl or Broken Link Finder against staging. - Verify each one resolves to the expected
new_urlwith HTTP 301 (single hop, not chained). - Flag any URL where the actual destination doesn't match the map. These need fixing before launch.
- Verify the new URL itself returns 200 (not another redirect or 404).
For sites with thousands of URLs, automate this — a small script that reads the CSV, hits each old URL, and reports failures. It's a one-evening job and pays for itself many times over.
Step 5: Pre-Launch Crawl
Before flipping DNS, crawl the new site as a search engine would:
- Verify all internal links use the new URL structure (no internal links pointing to the old URLs that just redirect).
- Check the sitemap.xml lists only canonical new URLs, no redirects.
- Ensure canonical tags point to the right URL on each page.
- Confirm no orphaned pages — every page is reachable from the homepage in a reasonable number of clicks.
Broken Link Finder handles the link verification piece; for full-site crawls, use Screaming Frog or a similar dedicated crawler.
Step 6: Launch and Verify
On launch day:
- Flip DNS during low-traffic hours. The first hours of a migration are when problems surface; you want minimal traffic exposure.
- Run the redirect test against production immediately. Same script as step 4 but pointing at the live URLs. Confirm every old URL returns 301 to the right destination.
- Submit the new sitemap to Google Search Console. Speeds up discovery of the new structure.
- Use the URL Inspection tool on a sample of high-value old URLs to verify Google sees the redirect and is indexing the new destinations.
- Monitor Search Console's Coverage report daily for the first two weeks. Look for spikes in crawl errors, soft 404s, or 4xx responses.
Step 7: Post-Launch Maintenance
Migrations are never done at launch. The first 4-8 weeks need ongoing attention:
- Watch organic traffic by URL. Pages that lost more than ~30% traffic compared to pre-migration baseline need investigation. Usually it's a redirect that points to the wrong page, or a content equivalence problem.
- Re-run the redirect audit weekly for the first month. New 404s sometimes surface as Google discovers URLs nobody included in the inventory.
- Track the Search Console Coverage report. The "Excluded" tab will fill with redirects (expected) and may surface soft 404s (problems).
- Update internal links. Every old internal link that points to a redirected URL is one extra hop your visitors and crawlers take. Update them.
- Reach out to high-value backlinkers if any old inbound link points somewhere broken. Most won't update, but some will.
The Mistakes That Wreck Migrations
- Treating the public sitemap as the complete URL inventory. It almost never is.
- Mass-redirecting to homepage. Soft 404 from Google's perspective; loses all ranking signal.
- Chained redirects. Old URL → temporary URL → new URL. See our redirect chains guide.
- Using 302 instead of 301. See our 301 vs 302 guide.
- Skipping the staging test. Production is a terrible place to discover the redirect map has bugs.
- Not testing the new URLs themselves. Redirects work but the destinations 404. Catch this before launch.
- Forgetting subdomains.
blog.example.comis a separate redirect concern fromexample.com/blog/. - Forgetting
wwwvs apex. Both should resolve to the canonical host with a single redirect. - Forgetting case sensitivity.
/About-Us/and/about-us/are technically different URLs to nginx; both should redirect. - Forgetting trailing slashes.
/pageand/page/should both resolve to one canonical form.
The Migration Checklist
Before launching:
- ☐ Complete URL inventory built from sitemap, CMS, logs, GSC, GA, and backlink data
- ☐ Every URL mapped to a destination (or marked as 410 Gone)
- ☐ No URL redirected to homepage
- ☐ Redirect rules generated and in version control
- ☐ Redirect test run against staging — every old URL → expected new URL with HTTP 301
- ☐ All redirects are single-hop
- ☐ New URLs return 200, not redirects
- ☐ Sitemap.xml lists canonical new URLs only
- ☐ Internal links updated to new URLs
- ☐ Canonical tags correct on every page
- ☐ Broken Link Finder scan of staging shows clean
After launching:
- ☐ Same redirect test run against production
- ☐ New sitemap submitted to Search Console
- ☐ URL Inspection on sample of high-value old URLs
- ☐ Daily Coverage report check for two weeks
- ☐ Weekly redirect audit for the first month
- ☐ Traffic monitoring by URL for 8 weeks
Migrations done this way are boring — which is exactly the goal. A boring migration is a successful migration. Run a final Broken Link Finder scan a week after launch and confirm everything still works; that's typically when subtle issues surface.