How to migrate a site without losing rankings
Most ranking losses after a migration are not mysterious. A redirect was missed, a canonical still points at the old host, or robots.txt from staging went live. The pages that earned the traffic stop answering at the URL Google knows, and Google drops them. I have seen all three on real moves, and none of them needed a clever fix. They needed someone to check before launch.
This tutorial is for operators moving a site to a new domain, a new URL structure, a new CMS or new hosting. You might run your own sites or look after a client’s. The outcome is a repeatable process: snapshot what you have, map every old URL to a new one, test on staging, launch, and watch Search Console for the weeks that matter. Expect a short wobble even when you do everything right. The goal is to keep it small and to recover fast.
One caveat first. A migration that changes content, design and URLs all at once makes problems impossible to diagnose. If you can, change one thing at a time.
What you need
- access to the old site’s DNS, hosting and CMS, plus the new one’s
- Google Search Console verified for the old domain (a domain property is best) and, if the domain changes, for the new one
- Screaming Frog SEO Spider (screamingfrog.co.uk/seo-spider). the free version crawls 500 URLs, the paid licence is around £199 a year at the time of writing. check the current price on their site
- a spreadsheet (Google Sheets or Excel) for the redirect map
- server access to write redirects (nginx, Apache, Cloudflare rules or your CMS redirect plugin)
- analytics export or rank tracker data for the baseline. a tracker that you trust matters here, and rank trackers disagree, so pick one and stick to it
- curl, which ships with macOS, Linux and current Windows
- a launch window with low traffic, and a rollback plan you have actually read
Budget: for a small site, the only paid item is the crawler licence. Bigger sites may want a log file analyser, but I would not buy one for a migration under a few thousand URLs.
Step by step
1. Snapshot the old site
Action: crawl the whole old site with Screaming Frog and export the internal HTML list. Then export from Search Console (Performance, pages, last 16 months) and from analytics. Save everything in a dated folder. Also copy robots.txt, the XML sitemaps and any existing redirect rules.
Expected output: one spreadsheet with every indexable URL, its status code, title, canonical, and clicks and impressions from Search Console. This is your baseline, and you will compare against it for months.
If it breaks: if the crawl misses pages, they are probably orphaned or JavaScript rendered. Add URLs from the sitemap, from Search Console and from analytics landing pages to the crawl list. Orphan pages with traffic still need redirects.
2. Rank the pages by what they earn
Action: add columns for clicks, referring domains and whether the page has external links. Sort by clicks. The top 50 or so pages are your protected list. Check these by hand at every stage after this.
Expected output: a short list of pages where a mistake costs real money. For everything else, rules and sampling are enough.
If it breaks: if you have no link data, export the linked pages report from Search Console (Links, top linked pages). It is less complete than a paid tool but free and it comes from Google itself.
3. Build the redirect map
Action: in a sheet, put old URL in column A and new URL in column B. One to one wherever the content survives. Where a page is being retired, point it to the closest relevant page, not the homepage. Mass redirects to the homepage tend to be treated as soft 404s, so they are worth little.
If you are changing domain, the same logic applies to the link equity side of things, which I cover in what happens to your links when you change domain.
Expected output: every old indexable URL has exactly one destination, and no destination is itself a redirect.
If it breaks: duplicate or looping rows show up fast with a pivot table. Count the rows per old URL, fix any with more than one target, and check that no value in column B also appears in column A.
4. Write the redirect rules
Action: turn the map into server rules. For pattern changes (a folder rename, say), use a regex rule. For one-offs, use a map. Use 301 for permanent moves. Google documents the behaviour of permanent redirects in its redirects guidance.
An nginx example for both cases:
# pattern: /blog/2021/some-post -> /articles/some-post
location ~ ^/blog/\d{4}/(.+)$ {
return 301 https://www.example.com/articles/$1;
}
# one-offs loaded from a map file
map $request_uri $new_uri {
include /etc/nginx/redirects.map;
}
server {
if ($new_uri) { return 301 $new_uri; }
}
and the map file format:
/old-page/ /new-page/;
/old-category/ /new-category/;
Expected output: redirects live on staging, or ready to deploy on launch, with the rules held in version control.
If it breaks: nginx will refuse to reload if the syntax is wrong. Run nginx -t before every reload. On WordPress with a redirect plugin, thousands of rules slow every request. Move them to the server config.
5. Test the redirects on staging
Action: make a plain text file of old URLs and run it through a loop. Check each returns a single 301 hop to the right place with a 200 at the end.
while read url; do
curl -s -o /dev/null -L -w "%{http_code} %{num_redirects} %{url_effective}\n" "$url"
done < old-urls.txt
Better still, use Screaming Frog in list mode and tick “always follow redirects”, then export the redirect chains report.
Expected output: every line says 200 1 and shows the expected new URL. Anything with num_redirects above 1 is a chain, and anything with a 404 is a miss.
If it breaks: chains usually come from stacking http to https, non-www to www and the page move as three separate rules. Collapse them so each old URL jumps once.
6. Check the new site’s on-page signals
Action: crawl staging and compare it to the baseline. Look at titles, meta descriptions, headings, canonicals, hreflang, structured data and internal links. Internal links should point straight at new URLs, not at old URLs that redirect. Make sure robots.txt does not block anything and that no page carries a noindex tag left over from staging. If your new structure adds filters or paginated lists, handle them deliberately. I wrote up pagination and faceted navigation because that is where migrations quietly create thousands of crawlable junk URLs.
Expected output: a diff showing that titles and canonicals match the old site, apart from the host and path changes you meant to make.
If it breaks: if staging is password protected, Screaming Frog can crawl with saved credentials. If it blocks bots by IP, whitelist your own address for the test.
7. Prepare the sitemaps and Search Console
Action: generate the new XML sitemap with new URLs only. Keep the old sitemap reachable for a while (listing old URLs that now redirect) so Google finds the redirects quickly. If the domain changes, add and verify the new domain as a property before launch.
Expected output: both properties verified, new sitemap ready to submit.
If it breaks: DNS verification failing is usually a TXT record sitting on the wrong host or still propagating. Check it with nslookup -type=TXT example.com.
8. Launch and verify the same day
Action: deploy the redirects and the new site. Then run the full old URL list again against production with the curl loop. Fetch the homepage, a top page and a redirected page in the URL Inspection tool. Submit the new sitemap. Update the old robots.txt so it does not block the old URLs, because Google needs to crawl them to see the redirects.
For a domain move, use the Change of Address tool in Search Console (see Google’s site move guide for the full sequence and when the tool applies). It only works for domain changes, not for path changes or http to https.
Expected output: all protected pages return 200 at the new URL, and the inspection tool shows the new canonical.
If it breaks: if the wrong thing went live, roll back first and debug second. A fifteen minute rollback costs less than a day of broken redirects being crawled.
9. Monitor for six to eight weeks
Action: every few days, check the Pages report for new errors, the Crawl stats report for spikes in 404s and compare clicks for your protected list against the baseline. Fix 404s that have traffic or links by adding the missing redirect. Keep the redirects in place for at least a year, which matches Google’s own guidance for site moves.
Expected output: impressions move across to the new URLs over a few weeks and the old ones fade. Totals typically dip, then recover. If the numbers look strange mid-move, Search Console numbers often do not add up because of property splits and reporting lag, so compare like for like before panicking.
If it breaks: if a protected page lost its rankings and is not redirecting properly, fix that first. If it redirects fine and still sits lower, give it time and check whether its content changed in the move.
Common pitfalls
- redirecting everything to the homepage: it saves time and throws away the relevance of every deep page. map pages to their closest equivalent.
- leaving staging directives live: a
Disallow: /or a site-wide noindex copied over from staging is the classic launch day disaster. check robots.txt and a page’s source within minutes of going live. - changing too much at once: new domain, new design, new copy and new URL structure in a single weekend means you cannot tell which change hurt you. split the work if you can.
- dropping the old redirects after a few months: links and bookmarks keep pointing at the old URLs for years. keeping the rules costs almost nothing.
- forgetting the non-HTML assets: PDFs, images and feeds that earn links or traffic need redirects too. the crawl will not list them unless you ask it to.
Scaling this
At 10x (a few hundred to a few thousand URLs), the spreadsheet approach still works. You spend more time on the protected list and rely on pattern redirects rather than one-offs.
At 100x (tens of thousands of URLs), manual mapping stops being realistic. You build the map programmatically from a database key shared by the old and new systems, test redirects with a script on every deploy and look at server logs to see how Googlebot actually responds. Crawl budget starts to matter, so clean up junk URLs before the move, not after.
At 1000x (millions of URLs, or many sites at once), you stage the migration by section, so a bad rule hits one folder and not everything. You need a dashboard for redirect health, an owner for each section and a freeze on other site changes. When I manage several properties in parallel, the real constraint is keeping logins and access separated per site, which is what the multiaccountops blog is about.
Where to go next
- how to find and fix keyword cannibalisation: merging old pages during a move often creates overlap you will want to clean up afterwards
- how to do an seo content refresh that actually works: once the move has settled, this is the next safe lever
- the blog index: everything else I have written on this site
This is not legal advice, and your hosting and registrar terms apply, so read them before you change DNS.
Written by Xavier Fok
disclosure: this article may contain affiliate links. if you buy through them we may earn a commission at no extra cost to you. verdicts are independent of payouts. last reviewed by Xavier Fok on 2026-10-03.