← All posts

CMS Migration SEO Guide: How to Move Your Site Without Losing Rankings

Quick answer: Sites lose rankings during a CMS migration for boring, preventable reasons: URLs changed without redirects, metadata got dropped, or content was mangled in transfer. The fix is procedural, not clever. Inventory every URL before you touch anything, map old to new 1:1, migrate content and metadata deliberately, and verify everything after launch. This post is that procedure, with code.

I've been on the cleanup crew for a couple of botched migrations, and the pattern is always the same: the team focused on the new platform and treated the old site as disposable. Google, unfortunately, has years of history invested in the old site's URLs. Here's how to move without torching it.

Why Migrations Kill Rankings

Google ranks URLs, not brands. Every URL on your current site has accumulated signals: backlinks pointing at it, internal links, indexed content, and a history of satisfying searchers. A migration threatens all of that at once.

The failure modes, roughly in order of how often I see them:

Failure What happens
Missing redirects Old URLs 404, link equity evaporates
Redirect chains or wildcards Everything redirects to the homepage, Google treats it as soft-404
Dropped metadata Titles and descriptions regenerate as defaults
Content changes bundled in Can't tell whether the drop is the migration or the rewrite
Blocked crawling Staging robots.txt or noindex tags shipped to production

That last one sounds absurd until you've seen a site launch with Disallow: / copied from staging. Check for it. Every launch.

The single most important rule: change one thing at a time. If you're migrating CMS, don't also redesign the templates, rewrite the content, and restructure the URLs in the same release. When rankings move, you need to know why.

Step 1: Inventory Every URL Before You Touch Anything

You cannot redirect URLs you don't know exist. Before any migration work starts, build a complete inventory from every source you have:

  • Your XML sitemap
  • A full crawl of the live site
  • Google Search Console's page and performance reports (catches URLs earning traffic that your sitemap forgot)
  • Analytics landing pages for the last 12 months
  • Your backlink report, so you know which URLs have external links worth preserving

For each URL, capture the things you'll need to preserve or verify later: title, meta description, canonical, and status code. Here's a simple inventory script that walks a sitemap and records metadata via the link.sc fetch API:

import csv
import requests
import xml.etree.ElementTree as ET

API_KEY = "lsc_your_api_key"
SITEMAP = "https://oldsite.com/sitemap.xml"

ns = {"sm": "http://www.sitemaps.org/schemas/sitemap/0.9"}
tree = ET.fromstring(requests.get(SITEMAP).content)
urls = [loc.text for loc in tree.findall(".//sm:loc", ns)]

with open("url_inventory.csv", "w", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(["url", "title", "description", "canonical", "word_count"])
    for url in urls:
        resp = requests.post(
            "https://link.sc/v1/fetch",
            headers={"Authorization": f"Bearer {API_KEY}"},
            json={"url": url, "format": "markdown"},
        )
        data = resp.json()
        meta = data.get("metadata", {})
        writer.writerow([
            url,
            meta.get("title", ""),
            meta.get("description", ""),
            meta.get("canonical", ""),
            len(data.get("content", "").split()),
        ])

That CSV is your source of truth for the entire project. Date it, version it, and don't let anyone launch until every row is accounted for.

Step 2: Map Redirects 1:1

If URLs are changing (new CMS, new structure, new anything), build a redirect map: one row per old URL, one new destination each.

The rules that matter:

  • 301, not 302. Permanent redirects pass ranking signals; temporary ones say "keep the old URL indexed."
  • 1:1 wherever possible. Redirecting a specific article to a category page or the homepage is treated as a soft 404. If a true equivalent doesn't exist, decide deliberately: closest relevant page, or let it 404 and reclaim the crawl budget.
  • No chains. Old URL to new URL directly, not through two hops of legacy redirects. Audit your existing redirects while you're in there.
  • Preserve the winning URLs at all costs. Sort your inventory by traffic and backlinks. The top 20% of URLs carry most of your equity; hand-check every one of them.

If the new CMS lets you keep the old URL structure, seriously consider it. The best redirect map is the one you don't need.

Step 3: Migrate Content Without Mangling It

CMS export/import tools are where content goes to die. Database exports carry old shortcodes, inline styles, and template artifacts into the new system, and the result renders subtly wrong in a hundred places nobody checks.

A cleaner approach for content-heavy sites: fetch the rendered pages of the old site as markdown, then import that into the new CMS. You migrate what visitors and Google actually saw, not what the old database happened to store.

curl https://link.sc/v1/fetch \
  -H "Authorization: Bearer lsc_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://oldsite.com/blog/important-post",
    "format": "markdown"
  }'

Loop that over the inventory and you have a clean, portable copy of every page: headings, links, and body content in a format any modern CMS can ingest. This also gives you a permanent archive of the pre-migration site, which is worth its weight in gold when someone asks "what did this page say before?" six months later.

While migrating, preserve metadata exactly: titles, meta descriptions, canonicals, structured data, and image alt text should transfer verbatim from your inventory CSV, not regenerate from the new CMS's defaults. This is precisely the on-page data the inventory captured, and the same techniques from my SEO analytics API post work for verifying it on the new side.

Step 4: Launch and Verify

Launch day checklist, in order:

  1. Confirm robots.txt allows crawling and no sitewide noindex shipped from staging.
  2. Spot-check the top 50 URLs by traffic: correct content, correct title, 200 status.
  3. Run the full redirect map: every old URL should return a single 301 to the right destination.
  4. Submit the new sitemap in Search Console; keep the old sitemap live briefly so Google recrawls old URLs and sees the redirects.
  5. Re-run the inventory script against the new site and diff it against the pre-migration CSV. Missing descriptions and truncated pages show up immediately in the diff.

Then monitor. Watch Search Console's coverage report for 404 spikes. If you want to confirm Google is picking up the new URLs, scripted site: checks work too, though run them through a search API rather than scraping results yourself (here's why). And watch rankings for your core keywords daily for the first few weeks. Some ranking turbulence for a couple of weeks is normal while Google reprocesses the site; a sustained drop past that point means something on this checklist got skipped. If you're tracking positions programmatically, my rank tracking API guide shows how to set up a daily check in an afternoon.

The Compressed Checklist

  • Full URL inventory from sitemap, crawl, Search Console, analytics, backlinks
  • Metadata snapshot of every page, stored in version control
  • 1:1 301 redirect map, hand-checked for the top pages
  • Content migrated from rendered pages, not raw database exports
  • Metadata transferred verbatim, not regenerated
  • Launch-day robots.txt and noindex check
  • Post-launch diff of new site against the inventory
  • Daily rank and 404 monitoring for a month

None of this is glamorous. All of it is cheaper than rebuilding two years of lost rankings.


Need to inventory and archive a site before migrating? Sign up for link.sc and fetch every page to clean markdown, 500 free credits a month.