← All posts

Site migration SEO checklist, from redirect map to post-launch


A site migration keeps its search traffic when every old URL answers with one permanent redirect to its closest new equivalent, nothing from staging leaks into production, and a baseline exists to judge the result against. The checklist below is organized around those three conditions, in phases, with a gate at the end of each one that has to pass before the next begins.

Set expectations with Google's own wording. Its guide to moving a site with URL changes says a small to medium-sized site can take a few weeks for most pages to move, larger sites take longer, and visibility can fluctuate while Google recrawls. A careful migration makes that period shorter and shallower. It doesn't make it disappear.

Name the kind of migration first

Different moves carry different risks, and bundling them multiplies those risks. Google's guide recommends making big changes one at a time, for example moving to the new domain first and changing the layout afterwards.

ChangeDo URLs change?Main risk
New domainEvery URLIncomplete redirects, and the old domain expiring later
HTTP to HTTPSEvery URL's protocolCanonicals, sitemaps and internal links left on HTTP
URL restructure on one domainSome or allMissing redirects and chains
Subdomain into a subfolderAll URLs on the subdomainTwo hosts' robots.txt, sitemaps and properties to reconcile
New platform, same URLsNoneTemplates that drop links, structured data or speed
New platform, new URLs and a redesignMostToo many variables to diagnose any drop

The phases at a glance

PhaseGate before moving onUsual owners
1. BenchmarkBaseline data stored somewhere that won't expireSEO and analytics
2. Redirect mapEvery URL with traffic, links or rankings has a reviewed destinationSEO and content owners
3. Staging QAParity crawl shows no unexplained differences and the launch blocker passesSEO and engineering
4. Launch dayRedirects, robots.txt, noindex and canonical checks pass on productionEngineering, SEO on call
5. First 48 hoursNo unmapped 404s or error spikes served to GooglebotSEO and engineering
6. Weeks 1 to 8Old URLs leaving the index, new ones arriving, any traffic gap explainedSEO

Phase 1, benchmark before anything moves

After launch someone will ask whether a dip is normal, and only a baseline can answer that. Capture it while the old site is live.

  • Store your Search Console Performance data outside the interface. The reports keep a rolling 16 months and cap direct exports at 1,000 rows, so pull page and query data through the API, or switch on the bulk export to BigQuery well ahead of launch. Google's bulk export documentation notes that it only collects data from the day you set it up, so older history has to come from the API or the reports.

  • List the URLs that matter: organic landing pages by traffic and revenue, pages with external links, and pages ranking for your most valuable queries.

  • Save a full crawl of the old site with titles, meta descriptions, canonicals, hreflang, structured data, indexability and internal link counts per URL. Once the old site is gone, this crawl is the only record of how it was built.

  • Record speed for key templates, preferably from field data.

  • Inventory which templates output which structured data types.

  • Verify every variant of the old and new sites in Search Console (www and non-www, HTTP and HTTPS), as Google's site move guide asks, and keep the old properties verified after the move.

  • Choose the launch window. Google suggests moving during a recurring traffic dip. Avoid launching just before a weekend or holiday, when nobody is around to read the logs.

Move on once the baseline exports are stored, the old-site crawl is saved and the launch date is agreed.

Phase 2, build the redirect map

The redirect map is the core deliverable. Build the inventory of old URLs from every source, because each one catches URLs the others miss: a crawl, the XML sitemaps, Search Console's page reports, analytics landing pages over a long date range, the backlink targets, and server logs, which surface old URLs Googlebot still requests even though nothing links to them anymore (the log file analysis primer covers how to extract them).

Then map them by these rules:

  1. Map one to one wherever the new site has an equivalent page.

  2. Tier the map. Pages with traffic, links or rankings get checked by hand; the long tail can be mapped by pattern rules and sampled.

  3. Where no equivalent exists, choose the closest relevant parent. Google warns that redirecting many old URLs to one irrelevant destination, such as the home page, can be treated as a soft 404. Redirecting several pages that were merged into one new page is fine.

  4. Let content that is gone for good return 404 or 410, which is what Google's site move guide asks for removed content.

  5. Write explicit rules for query strings. Parameters that select content (a category ID, a product variant) need their own destinations; tracking parameters should pass through or be dropped on purpose.

  6. Have someone who knows the content review the map, not only someone who knows the URLs. Pattern rules produce confident mistakes at scale.

Redirect rules

Google's redirects documentation splits redirects into permanent and temporary types, and the difference decides which URL appears in results.

ResponseHow Google treats itUse it for
301Permanent; a signal the target should be canonical, target shown in resultsEvery migrated URL
308Permanent, same as 301 for Google, keeps the request methodPermanent moves where form posts must survive
302, 303, 307Temporary; Google keeps showing the source URLShort detours you will undo
Meta refresh, 0 secondsTreated as permanentPlatforms with no server access
Meta refresh, delayedTreated as temporaryAvoid for moves
JavaScript locationPermanent, but seen only if rendering succeedsLast resort
404 or 410URL dropped from the indexContent gone with no equivalent

A few rules follow from that table and from Google's site move guide:

  • Redirect in one hop. Googlebot can follow up to 10 hops, but Google advises redirecting to the final destination directly, and if that's impossible, keeping chains to ideally no more than 3 and fewer than 5. Previous migrations leave layers behind, so update those old rules to point at the new final URLs too.

  • Don't use a temporary redirect for a permanent move. The source URL stays in results, and there is no documented point at which Google starts treating a 302 as a 301.

  • Pick 404 or 410 for clarity, not speed. Google's HTTP status code documentation says all 4xx errors except 429 are treated the same.

  • Keep the redirects for at least a year, which is Google's general guidance, and longer if old URLs still get visits or links. Google's site move guide also states that 301 redirects don't cause a loss in PageRank, so a clean one-hop 301 doesn't tax your signals.

Normalizing protocol and host in the same rule as the path mapping is what keeps the hop count at one. A generic nginx example for a move from example.com to www.example.net:

map $request_uri $new_path {
    default                    "";
    /shop/trail-shoe           /products/trail-running-shoe;
    /blog/2019/sizing-guide    /guides/shoe-sizing;
}

server {
    listen 80;
    listen 443 ssl;    # certificate lines omitted
    server_name example.com www.example.com;

    if ($new_path) {
        return 301 https://www.example.net$new_path;
    }
    return 301 https://www.example.net$request_uri;
}

Map keys match the full request URI, query string included, so parameter handling needs its own decision.

Test the full map against staging before launch. This shell loop reads a CSV of old URLs and expected destinations (with the old host pointed at staging) and reports anything that isn't a single permanent hop to the right page. It uses normal GET requests rather than curl's HEAD mode, because some servers and CDNs answer HEAD requests differently.

#!/bin/sh
# Usage: sh check-redirects.sh redirect-map.csv
# Each line: old_url,expected_final_url
while IFS=, read -r old expected; do
  first=$(curl -s -o /dev/null -w '%{http_code}' "$old")
  set -- $(curl -s -o /dev/null -L --max-redirs 10 \
    -w '%{http_code} %{num_redirects} %{url_effective}' "$old")
  code=$1 hops=$2 final=$3
  if [ "$first" != 301 ] && [ "$first" != 308 ]; then
    echo "NOT PERMANENT ($first): $old"
  elif [ "$hops" -gt 1 ]; then
    echo "CHAIN ($hops hops): $old -> $final"
  elif [ "$final" != "$expected" ]; then
    echo "WRONG TARGET: $old -> $final (expected $expected)"
  elif [ "$code" != 200 ]; then
    echo "TARGET RETURNS $code: $final"
  fi
done < "$1"

Move on once every URL with traffic, links or rankings has a reviewed destination and the script reports nothing for them.

Subdomain or subfolder, decided before the move

If the project consolidates a subdomain into the main site, or you are choosing where a new blog, shop or help center should live, settle the structure first. Google representatives have said publicly that Search handles both subdomains and subfolders, and Google's international site guidance lists practical pros and cons of each structure without naming a winner. Many practitioners report better results after moving content from a subdomain into a subfolder of an established domain. Those reports are not controlled experiments, and the moves often came with other changes, so treat a consolidation gain as plausible rather than promised, and weigh it against the cost of a migration.

SituationStructure that usually fitsWhy
Blog, guides or help center for the same audienceSubfolderShares the main site's links and navigation, one host to maintain
Separate product, audience or purpose (forum, status page, web app)Subdomain or separate domainSeparation is the point, often with a different stack and owner
Platform that can't run in a subfolderSubdomain now, reverse proxy later if justifiedShipping beats waiting; a proxy can come later
Country or language versionsSubfolders by default, country domains where local trust mattersCovered in the hreflang and international URL structure guide
Existing subdomain that performs wellLeave it unless there is a business caseA migration has its own cost and risk

When a reverse proxy serves another platform under a subfolder, check the plumbing:

  • Canonicals, hreflang and internal links on proxied pages use the main host's subfolder URLs, never the platform's own hostname.

  • The platform's own hostname doesn't stay publicly crawlable as a duplicate. Restrict it to the proxy, or make every page on it canonicalize to the subfolder URL.

  • Robots.txt is read per host, so any rules the platform needs move into the main host's file.

  • The subfolder's XML sitemap lives on the main host and is listed in the main robots.txt.

  • Status codes and headers survive the proxy. A missing page should return 404 through the proxy, not a 200 error page, and X-Robots-Tag headers should arrive intact.

  • Keep the old subdomain's Search Console property verified so you can watch its URLs drain.

Phase 3, staging QA and the launch blocker

Protect staging with authentication (HTTP auth or an IP allowlist) instead of noindex tags or robots.txt rules. A password can't be copied to production by accident the way a meta tag can. Google's guide also reminds you to remove any noindex or robots.txt blocks that were only needed during the migration.

With staging up, run a parity crawl and diff it against the saved crawl of the live site, template by template: titles, meta descriptions, canonicals, hreflang, structured data, indexability and internal link counts to priority pages. Every unexplained difference is either an approved change or a bug. Crawl once with a smartphone user agent as well, since Google indexes the mobile version of pages. If the new site renders content client-side, compare the raw HTML with the rendered output for titles, canonicals and links, as described in the guide to JavaScript SEO.

Then make the staging leftovers impossible to ship. This script can run in the deployment pipeline against production and fail the release if it finds a blocking robots.txt, a noindex in the HTML or headers, or a staging hostname in canonicals:

#!/bin/sh
# Launch blocker: exit 1 if production carries staging leftovers.
SITE="https://www.example.net"
PATHS="/ /category/shoes/ /products/trail-running-shoe/ /guides/shoe-sizing/"
fail=0

# Crude on purpose: flags any bare "Disallow: /" line for review.
if curl -s "$SITE/robots.txt" | tr -d '\r' | grep -Eiq '^disallow:[[:space:]]*/[[:space:]]*$'; then
  echo "robots.txt contains Disallow: /"; fail=1
fi

for p in $PATHS; do
  url="$SITE$p"
  curl -s -D headers.txt -o page.html "$url"
  if grep -i '^x-robots-tag:' headers.txt | grep -iq 'noindex'; then
    echo "X-Robots-Tag noindex on $url"; fail=1
  fi
  if grep -io '<meta[^>]*>' page.html | grep -Ei 'robots|googlebot' | grep -iq 'noindex'; then
    echo "meta noindex on $url"; fail=1
  fi
  if grep -io '<link[^>]*>' page.html | grep -i 'canonical' | grep -Eiq 'staging|localhost'; then
    echo "staging canonical on $url"; fail=1
  fi
done
exit $fail

It checks raw HTML only, so a canonical injected by JavaScript still needs the rendered check above.

Move on once the parity crawl shows no unexplained differences and the blocker passes against a production-like build.

Phase 4, launch day

Run this list in order on production:

  1. Fetch robots.txt: status 200, intended rules, no leftover Disallow: /.

  2. Check key templates for noindex in both the HTML and the X-Robots-Tag header.

  3. Run the redirect script on the prioritized sample, then on the full map.

  4. Confirm canonical tags on every template point at production URLs with the right protocol and host.

  5. Confirm navigation, footers, breadcrumbs and in-body modules link straight to new URLs instead of bouncing through redirects. Google's guide asks you to update internal links, and the internal linking guide covers auditing them at scale.

  6. Submit the new XML sitemaps and keep one listing the old URLs available for a while, as Google's site move guide suggests, so the redirects get discovered. Warnings about redirecting URLs in the old sitemap are expected. More on structure in the post on XML sitemaps that help.

  7. For a domain change, file a Change of Address in the old Search Console property. Google's Change of Address help says not to use it for HTTP to HTTPS moves, for moving pages within one site, for switching between www and non-www on the same domain, or for hosting changes without URL changes. The tool doesn't move subdomains below the domain you enter, including www.

  8. If the old property had a disavow file, upload it to the new property too.

  9. Spot-check structured data on key templates with the Rich Results Test.

  10. Check that hreflang return links point at new URLs in every locale.

  11. Confirm analytics tags fire and Search Console verification survived.

  12. Make sure missing pages return a real 404 status with a page that offers search and links to the main sections.

Phase 5, the first 48 hours

Watch server logs for 404 and 5xx responses served to verified Googlebot. A 404 on an old URL that had traffic or links means a gap in the map, and it should be patched the same day. Confirm that crawl activity is shifting from old URLs to new ones; the Crawl stats report in Search Console lags behind logs but confirms the trend. Watch capacity as well, because Google's status code documentation says 5xx and 429 responses make its crawlers slow down.

Keep a dated log of every fix. When the graphs wobble a week later, you will need to know whether the wobble came before or after a change.

Phase 6, weeks 1 to 8

Track the Page indexing report in both old and new properties: old URLs should move to "Page with redirect" while new URLs get indexed. Re-crawl the new site and diff it against the saved crawl, paying attention to titles, canonicals, structured data and internal links to the pages that earn money.

For domain changes and subdomain consolidations, judge the move by the combined clicks of old and new locations against the baseline, not by either one alone. Old URLs lose impressions as new ones gain them, and looking at only one side of that handover tells you very little.

Wait, investigate or roll back

What you seeLikely meaningResponse
Traffic dipped, old URLs leaving the index, new ones arrivingNormal reprocessingWait and keep watching
Googlebot hitting 404s on old URLs with traffic or linksGaps in the redirect mapPatch the map the same day
Redirects answering 302 or chainingServer or CDN rules misconfiguredFix the rules, rerun the script
New URLs barely crawled after several daysRobots.txt block, discovery problem or server limitsInvestigate now
noindex or Disallow: / on productionStaging leftoversTreat as an incident and fix immediately
Rising 5xx or 429 responsesInfrastructure can't take crawl plus trafficFix capacity; crawling slows until it's resolved
A template lost content, links or structured dataScope crept into the replatformRestore it before judging rankings
Core pages broken, fixes days away, old site intactSystemic failureConsider a rollback

A rollback is itself a migration in reverse, with redirects flipped and Google reprocessing everything again, so it only makes sense early, for failures the new platform can't fix quickly, and while the old site and its URLs are still intact.

Where traffic leaks after a clean launch

When the launch checks pass and traffic still sags, look here:

  • Redirects that strip query strings, so content-selecting URLs all land on one generic page that Google can treat as a soft 404.

  • A redesign that removed internal links, such as a collapsed mega menu, a trimmed footer or deleted cross-linking modules.

  • New templates that no longer output the structured data the old ones did.

  • Content changes bundled into "just a replatform": rewritten titles, thinner category copy, removed sections.

  • Heavier templates that slow pages down.

  • Pagination or filter changes that leave deep product pages with no path from the category.

  • Sections nobody owned, such as an old blog subdomain, a help center or a PDF library, left without redirects.

  • An old domain allowed to expire. Google's Change of Address help recommends paying for the old domain for at least a year so nobody else can buy it, and keeping it for as long as it holds redirects that receive visits is safer still.

If your launch date is already set, start with phase one this week. The Search Console export and the saved crawl are the two things you can't recreate once the old site is gone.