Technical SEO Checklist for Any CMS Migration

Technical SEO Checklist for Any CMS Migration

This is a checklist, not a narrative — the specific, concrete items that determine whether a CMS migration preserves your search rankings or quietly tanks them over the following month. Platform-agnostic; applies whether you're moving to or from any CMS.

Table of Contents
  1. Before Migration
  2. Build the Redirect Map
  3. During Migration
  4. Immediately at Launch
  5. The First 12 Months After Launch
  6. Frequently Asked Questions
  7. What's the single most common mistake on this checklist?
  8. Is a 302 ever acceptable during migration?
  9. How long should I actually wait before declaring the migration a success?
  10. The Bottom Line
  11. Sources

Before Migration

  • Export at least 12 months of Google Search Console data — you can't measure recovery or diagnose a ranking drop without a pre-migration baseline.
  • Export Core Web Vitals field data from CrUX before touching anything — post-launch performance claims are unverifiable without this.
  • Crawl and export your complete current URL list (a tool like Screaming Frog, or your current CMS's own export) — this becomes the source list for your redirect map.
  • Document current meta titles, descriptions, and canonical tags for every indexed page.
  • Identify your highest-traffic and highest-converting pages specifically — these get manual verification priority after launch, not just automated checks.

Build the Redirect Map

A 1:1 redirect map using 301 permanent redirects must be completed before launch, not after, to preserve the link equity built into every indexed URL on the old site. Use 301 redirects, not 302s — 301s transfer link equity to the new URL; 302s signal the move is temporary and equity transfer is not guaranteed.
  • Map every old URL to exactly one new URL — 1:1, not many-to-one unless genuinely consolidating pages.
  • Use 301 (permanent) status codes specifically, never 302 for a real, permanent migration.
  • Avoid redirect chains longer than 2 hops — each additional hop reduces equity passed and slows crawl speed.
  • Test every redirect before launch, not after — a broken redirect map found post-launch has already cost you crawl budget and user trust.
Read also:

During Migration

  • Stage the new site on a non-indexed environment first, and verify robots.txt/meta-robots settings before it goes anywhere near production.
  • Double- and triple-check that the staging environment's noindex directive does not carry over to the live launch — this specific mistake can cause index loss within days.
  • Migrate structured data (JSON-LD) alongside content, not as a follow-up task — don't launch without it if the old site had it.
  • Preserve or improve Core Web Vitals — verify against your pre-migration CrUX baseline, not just a lab test.
  • Update your XML sitemap to reflect only new URLs, and submit it to Search Console immediately at launch.

Immediately at Launch

  • Update internal links to point directly to new URLs — don't rely on redirects for your own internal linking, since that wastes crawl budget and risks redirect chains.
  • Verify canonical tags point to the correct new URLs, not leftover staging or old-domain values.
  • Confirm the redirect map is live and working on the actual production domain, not just staging.
  • Submit the new sitemap and request re-indexing of key pages in Search Console.
  • Check hreflang tags if the site is multilingual — these need updating alongside URL structure.

The First 12 Months After Launch

301 redirects should remain live for a minimum of 12 months after migration. During this window, search engines consolidate ranking signals to the new URLs and external sites re-crawl and update their links. Removing redirects before this period closes risks losing link equity and causing 404 errors for pages still being discovered via old links.
  • Keep every redirect live for a full 12 months minimum — do not decommission the old URL structure early, even if it feels safe to.
  • Monitor Search Console weekly for the first month, then monthly — watch for crawl errors, sudden impression drops, or unexpected 404s.
  • Expect traffic to stabilize within 4-8 weeks for most sites, longer for larger sites — don't panic at week 2, but do investigate anything still declining at week 10.
  • Reach out to high-value external sites linking to old URLs and ask them to update to the new URL directly, rather than relying solely on the redirect.

Frequently Asked Questions

What's the single most common mistake on this checklist?

Carrying over a noindex meta tag from the staging environment to production — a simple oversight that can cause real, fast index loss, and one that's easy to catch with a single manual check before DNS actually flips.

Is a 302 ever acceptable during migration?

Only if the move is genuinely temporary (an A/B test, a short-term promotional redirect) — for a real, permanent platform migration, 301 is the correct choice every time, since 302s don't reliably transfer accumulated ranking signal.

How long should I actually wait before declaring the migration a success?

8-12 weeks at minimum, watching Search Console the whole time — traffic typically stabilizes in 4-8 weeks, but a slower recovery on a larger or more complex site isn't automatically a failure, just worth continued monitoring.

The Bottom Line

Most SEO-damaging CMS migrations fail on execution, not platform choice — a missing redirect, a stray noindex tag, or 301s decommissioned too early. Work through this checklist in order rather than treating any single step as optional. See our companion guide on the ranking-preservation mechanics behind these steps for the reasoning behind each item.

Sources

Share

Comments

Write a comment

Related Articles

Login Works in Production but Fails on localhost: The secure Cookie Flag Gotcha

July 16, 2026

Login Works in Production but Fails on localhost: The secure Cookie Flag Gotcha

Google Analytics (GA4) Not Tracking Anything and No Console Errors: Check Your CSP First

July 16, 2026

Google Analytics (GA4) Not Tracking Anything and No Console Errors: Check Your CSP First

Native Module Build Fails on Shared Hosting (node-gyp, Old glibc/Python): The .npmrc Fix

July 16, 2026

Native Module Build Fails on Shared Hosting (node-gyp, Old glibc/Python): The .npmrc Fix

Login Works in Production but Fails on localhost: The secure Cookie Flag Gotcha

Login Works in Production but Fails on localhost: The secure Cookie Flag Gotcha

TL;DR — A custom session-cookie login flow appeared to succeed on localhost (the OTP verified, the response looked fine) but every subsequent request to a login-gated page treated the visitor as logged out. Identical code worked fine on the live HTTPS site. The cookie's Secure attribute was hardcoded to true — and per the cookie spec, browsers never store or send a Secure cookie over a plain, non-HTTPS connection.

Table of Contents
  1. Diagnostic
  2. Root cause
  3. Fix
  4. Lessons learned

Diagnostic

Check the actual Set-Cookie response header and the browser's own cookie storage panel — on localhost over http://, the cookie is sent by the server but never actually stored by the browser.

Root cause

// before -- assumes the app is always served over HTTPS
setCookie("session", token, { secure: true, httpOnly: true });
emdashkits.com

A cookie config that quietly assumes "we're always on HTTPS" breaks the instant you test over plain HTTP, which local dev servers commonly are.

Read also:

Fix

// after -- derive secure from the actual request protocol
const isHttps = request.url.startsWith("https://");
setCookie("session", token, { secure: isHttps, httpOnly: true });
emdashkits.com

Lessons learned

  • Any Secure-flagged cookie needs to key off the real request scheme, not an assumption baked in once at cookie-creation time.
  • "Works in production, silently fails in local dev" is a strong signal to check cookie flags before anything else in an auth flow.
  • Check other cookies in the same codebase for the same hardcoded assumption — if one cookie has this bug, sibling cookies set the same way are worth auditing too.
Share
Previous Article

Google Analytics (GA4) Not Tracking Anything and No Console Errors: Check Your CSP First

Comments

Write a comment

Related Articles

Google Analytics (GA4) Not Tracking Anything and No Console Errors: Check Your CSP First

July 16, 2026

Google Analytics (GA4) Not Tracking Anything and No Console Errors: Check Your CSP First

Native Module Build Fails on Shared Hosting (node-gyp, Old glibc/Python): The .npmrc Fix

July 16, 2026

Native Module Build Fails on Shared Hosting (node-gyp, Old glibc/Python): The .npmrc Fix

Uploaded Images Disappear After Every Deploy on Shared Hosting (and Other Reverse-Proxy Gotchas)

July 16, 2026

Uploaded Images Disappear After Every Deploy on Shared Hosting (and Other Reverse-Proxy Gotchas)

Google Analytics (GA4) Not Tracking Anything and No Console Errors: Check Your CSP First

Google Analytics (GA4) Not Tracking Anything and No Console Errors: Check Your CSP First

TL;DR — GA4's gtag.js snippet was installed correctly, the page loaded with no visible JavaScript error, and GA4's real-time report still showed zero activity. The CMS's default Content-Security-Policy had no allowance for analytics domains and no config option to add one — so every request to Google's tracking endpoints was blocked at the browser level before it could fail loudly.

Table of Contents
  1. Diagnostic
  2. Root cause
  3. Lessons learned

Diagnostic

Check the browser's dedicated CSP violation reporting, not the regular console error list — CSP blocks are reported through their own channel, not thrown as normal script errors, so "no console errors" doesn't mean nothing was blocked.

Root cause

The CSP's script-src and connect-src directives had no entry for googletagmanager.com or google-analytics.com, and the CMS exposed no configuration surface to add one — the only way in was patching the CSP directives directly.

// patch-package: add analytics domains to the existing CSP directives
scriptSrc.push("https://www.googletagmanager.com");
connectSrc.push("https://www.google-analytics.com", "https://www.googletagmanager.com");
emdashkits.com
Read also:

Lessons learned

  • "No console errors" is not proof nothing was blocked — CSP violations live in their own reporting surface and are easy to miss if you're only scanning for red error text.
  • Before adding any third-party script tag to a site with a CSP already in place, check the CSP's directives first rather than assuming a silently-empty analytics dashboard means a snippet-installation mistake.
Share
Previous Article

Native Module Build Fails on Shared Hosting (node-gyp, Old glibc/Python): The .npmrc Fix

Next Article

Login Works in Production but Fails on localhost: The secure Cookie Flag Gotcha

Comments

Write a comment

Related Articles

Login Works in Production but Fails on localhost: The secure Cookie Flag Gotcha

July 16, 2026

Login Works in Production but Fails on localhost: The secure Cookie Flag Gotcha

Native Module Build Fails on Shared Hosting (node-gyp, Old glibc/Python): The .npmrc Fix

July 16, 2026

Native Module Build Fails on Shared Hosting (node-gyp, Old glibc/Python): The .npmrc Fix

Uploaded Images Disappear After Every Deploy on Shared Hosting (and Other Reverse-Proxy Gotchas)

July 16, 2026

Uploaded Images Disappear After Every Deploy on Shared Hosting (and Other Reverse-Proxy Gotchas)