Forty-two ranking URLs 404 on the first launch crawl, and twelve more 301 into a loop back to themselves. That is the usual mess after a domain or CMS cutover that skipped the SEO work and went straight to DNS.

We treat website migration SEO as a sequence, not a weekend. Hostname, CMS, or both - the shape does not change. Map every URL that currently earns impressions, decide its destination, prove the redirect, then crawl the live host after cutover. Skip a step and Search Console spends the next month teaching us what we already knew on staging.

This seo migration checklist is the order we actually run. Sign the URL map off before anyone drops TTL. If the map is still a tab in someone’s head, we do not move DNS.

Freeze the URL map before DNS

The URL map is the job. Everything else is enforcement.

We export the last sixteen months from Search Console: page, clicks, impressions, queries. We crawl the live host and keep every 200, every 301 that already exists, every PDF and image that ranks, every parameter URL that still picks up impressions. Then we match. Path first. Title second. Content hash if the CMS has rewritten slugs on export.

Unmatched rows stay unmatched. We do not invent destinations so the sheet looks complete. A ranking URL with no destination is a decision: 301 to the nearest equivalent, 410 if the product is gone, or rewrite the page on the new host before cutover. Blank cells at launch become 404s in the crawl.

We freeze the map. Design can still move buttons. Developers can still fix templates. Nobody adds a new public path without a row. If the CMS wants to mint `/blog/category/post-name` where we had `/post-name`, that is a map change, not a surprise on Monday.

When the sheet runs past a few hundred paths, or the CMS rewrites taxonomy on export, this stops being a cutover and becomes scoped website migration services. We still use the same map. We just stop pretending it fits in a Friday deploy.

Redirects live in a sheet, not in someone's head

One hop. Permanent. Tested.

301 for anything we mean to keep. 302 only if the destination is genuinely temporary, which on a migration it almost never is. Chains of two or three hops look tidy in a CMS plugin and waste crawl budget on the new host. We flatten them in the sheet before they hit the server.

We test from the old hostname, from http, from www, from the trailing-slash variant, and from the mobile user-agent if the old site still splits. A redirect that only works in Chrome on the office network is not a redirect.

Loops are the classic self-own. Old `/about` to new `/about-us`, new canonical still pointing at `/about`, server 301 back. The launch crawl will find it. Better if the sheet finds it first.

Query strings need a rule, not a wish. If old paid landing pages carried `?utm_` and the new CMS strips unknown parameters into 404, we lose the ranking URL and the ad destination in one night. Preserve, strip, or map - write it down.

We keep the redirect sheet after launch. Week two is when someone republishes an old path as a “nice URL” and breaks the 301.

Search Console properties and change of address

Both hosts need a property before DNS moves. Old domain, new domain, and the exact protocol and www variant we intend to keep. If we only verify the new host after cutover, we spend a week blind.

Sitemaps: last crawl of the old host, submit the new sitemap on the new property the hour the 200s are real. Leave the old sitemap in place until Google has seen the 301s; then retire it. Two live sitemaps pointing at two hosts is how we teach Google the site still lives in two places.

Change of Address is for a hostname move, not a CMS path rewrite on the same domain. Google’s site move with URL changes documentation is the floor for that tool, for sitemap resubmission, and for the crawl behaviour they expect after the swap. We still run our own URL map on top of it. The tool does not invent redirects we failed to ship.

International and language variants need the same property discipline. A `.ph` host moving to a subdirectory on a global domain is not “just DNS”. Hreflang, Search Console, and the map all have to agree on the final URL, or we index both and split the query.

Staging, robots.txt and the accidental index

Staging with a public hostname and an open robots.txt is how the new templates get indexed a month early, then 404 when we flip DNS.

We block staging. `noindex` on every template, robots disallow if the host must not be fetched, HTTP auth if the client can live with it. We check the live response headers, not the CMS checkbox. A plugin that says “noindex” while the rendered HTML serves `index,follow` is a staging lie.

The inverse failure is worse: we ship production with the staging `noindex` still in the header, or with `Disallow: /` left in robots.txt. The launch crawl must see `200` and indexable on the money pages in the first hour. If it does not, we roll the header back before we tweet the new URL.

XML sitemaps on staging should never be submitted to the production property. We have watched a staging sitemap of `/node/123` land in the live index because someone used the same Search Console user on both hosts.

Content parity after the CMS export

Redirects keep the URL. They do not keep the page.

We diff titles, H1s, word count, and primary internal links on the URLs that earned clicks. A CMS export that turns a 1,800-word service page into a 90-word card with a hero image is not a migration. It is a content deletion with a 301 on top.

Thin replacements, missing H1s, and stripped FAQs after export are a content problem. That is when content SEO services in the Philippines sit after the technical cutover, not instead of it. We do not wait three months to notice the body copy vanished.

Media is part of parity. Old `/wp-content/uploads/…` paths that rank for branded screenshots need a 301 to the new media host, or the image search listing dies and the article that embedded it 404s the file. Same for downloadable PDFs that sales still mail.

Internal links in the body need a pass. If the new CMS rewrites them to the new path, good. If it leaves absolute old-host links, we 301 those too - but we also fix the HTML, because a permanent site should not rely on its own redirects to navigate.

The launch crawl in the first four hours

We crawl the new host as soon as DNS answers. Not tomorrow. Not after the recap email.

First pass: status codes on the URL map. Every row that should be 200 is 200. Every row that should be 301 lands on the destination we signed off, in one hop. Soft 404s - 200 with “page not found” in the title - count as failures.

Second pass: indexation signals. `noindex` gone. Canonical host matches the live hostname, protocol, and slash rule. robots.txt allows the templates we care about. Sitemap URLs resolve.

Third pass: the money pages. Title, H1, primary CTA, form, and phone number. A migration that 301s perfectly and ships a contact form into a 500 is still a failed launch.

We sample Googlebot in the logs if we have them. A crawl tool is not Google. If Googlebot is still hitting the old host, TTL and CDN cache are the next ticket, not “wait for rankings”.

Screenshot the Search Console coverage and the first crawl report. Week two arguments about “what we launched with” die when the folder exists.

Canonicals, hosts and leftover protocols

The leftover www is the gift that keeps giving.

We pick one: https, apex or www, trailing slash or not. Then we 301 the other three. Canonical tags that point at a host we do not 301 are how we index duplicates for a quarter.

CMS platforms love to emit `rel=canonical` from the admin hostname, or from a `www` they no longer serve. We view source on a handful of templates, not the homepage only. Paginated lists, filters, and search-result URLs are where the wrong canonical hides.

Parameter handling belongs here. If the new site can be sorted `?price=asc` and the canonical does not collapse to the clean URL, we have just invented an indexable copy of every category. Faceted navigation is not a post-launch tidy. It is a map decision.

Hreflang, if we have it, must point at the final URLs. Old-host hreflang after cutover is a gift to the duplicate we just redirected.

Tracking, forms and the conversion gap

SEO can look fine while leads go to `/dev/null`.

Analytics property, tag manager container, conversion pixels, call tracking, and the form endpoint all need to fire on the new host. We test a real submission, not a pageview. Thank-you URLs that 404, or that 301 back to the homepage, destroy the conversion we were ranking to get.

Cross-domain if the form posts to a CRM host. Consent banners that block tags until click - check that the first landing from organic still records a session. Staging containers accidentally left on production will either double-count or send hits to a view nobody watches.

We keep the old Analytics view readable for a month. Comparing new-host landing pages to old-host landing pages is how we catch a template that dropped the primary keyword from the H1 and then blamed “the algorithm”.

Week two in the logs

Rankings wobble. That is normal. Silence in Search Console is not.

We watch coverage, not vanity positions. Spikes in 404, not-found in sitemap, and “crawled - currently not indexed” on URLs that were 200 on launch day. Those are the rows we reopen.

Log files tell us if Googlebot accepted the 301s or is still requesting the old paths in volume. High old-path request volume two weeks in means the redirect is wrong, cached, or missing on a CDN edge.

We recrawl the map. Plugins get updated. Someone publishes a new blog template that omits the canonical. Someone turns on a “related posts” module that links to staging paths. Week two is where the freeze on the URL map earns its keep.

If a cluster of pages drops together, we check content parity again before we touch redirects. A template that truncated body copy will look like a ranking loss and is actually a page we no longer have.

The next useful step

Print the map. If we cannot name the destination of the top 100 URLs by clicks, we are not ready for DNS.

If we can, we schedule the launch crawl in the same window as the cutover, not the next morning. We verify both Search Console properties. We test one real form. We watch robots.txt with our own eyes.

A hostname swap with a clean one-to-one path map is a disciplined cutover. A CMS change that rewrites taxonomy, drops body copy, and invents faceted URLs is a different job. Use this seo migration checklist until the sheet is honest. When the sheet is bigger than the team, or the export has already eaten the H1s, we scope an audit against the live host and the staging crawl and decide from the rows, not from a hope that Google will “figure it out”.