I've watched migrations fail spectacularly because someone decided redirects could be handled "later." They couldn't. When you move your site to a new domain or restructure your URL scheme, every old URL your users and search engines know about becomes a dead end—unless you build a redirect map before migrating. Without one, you lose ranking signals, referral traffic, and user trust in hours. This is the most critical SEO foundation you'll lay during any migration.
Export all URLs from logs, sitemaps and analytics
You need to know every URL that matters. Most teams only look at their current sitemap, and that's already a mistake. Your sitemap shows what you want indexed; your logs and analytics show what people actually visit.
Start with three sources:
- Sitemaps: Your XML sitemap and any historical sitemaps you still have. If you're migrating from a legacy system, dig those out from the Wayback Machine or your CDN archives.
- Web server logs: Parse your access logs from the past 12 months. Look for successful responses (200–299 status codes). A simple command gets you unique URLs quickly:
cat access.log | awk '{print $7}' | sort -u > all_urls.txt- Analytics: Export all landing pages from Google Analytics. Include pages with traffic, even if they're not indexed. Users found them for a reason.
Merge these lists, deduplicate, and count. You should see how many URLs you're actually responsible for. I worked on a CRM platform migration where we thought we had 3,000 pages; the logs revealed 47,000 dynamic URLs. We would've missed the other 44,000.
Once you have your master list, validate them. Check HTTP status codes, remove duplicates with query parameters (or keep them if they're distinct), and separate static content from dynamic routes. Document the source of each URL; that context matters when you decide what stays and what gets consolidated.
Map one-to-one wherever possible
The best redirect is one that takes users and search engines to the exact same content they expected. One-to-one mapping is that ideal. When your old URL structure mirrors your new one semantically, do the work to map them directly.
Real example: you're migrating from /blog/2024/01/caching-strategies to /blog/dotnet-caching. If both pages cover the same topic with the same target keyword, that's a one-to-one redirect. Set it up like this in your web server config:
location = /blog/2024/01/caching-strategies {
return 301 /blog/dotnet-caching;
}Why? Because the old path has accumulated links, bookmarks, and search visibility. When you redirect 301, you pass roughly 90% of that authority to the new URL. Search engines understand it's a permanent move.
But one-to-one mapping requires judgment. Not every old URL deserves a direct match. If you're consolidating five sparse blog posts into one comprehensive guide, you can't map all five to one new URL and expect it to feel natural to users. That's when you need to decide: does the merged content serve the same user intent? If yes, map all five to the new URL. If no, some might deserve a 404 with a link to the closest match.
Build a spreadsheet to track these decisions. Columns: old URL, new URL, redirect type (301, 404, 410), reason. When stakeholders question why a page got a 404, you have documentation. This discipline catches mistakes before launch.
Avoid redirecting everything to the homepage
I see this constantly: migrate the site, panic, redirect all old URLs to /. Users land on the homepage expecting their article. They leave. Google sees a thousand redirects pointing to the same destination. It tanks your rankings.
Redirecting to the homepage signals to search engines that the old URL wasn't important—because if it were, you'd know where it went. You're essentially telling them to drop that URL from their index and pretend it never existed. Meanwhile, users get a jarring experience and lose trust.
Instead, create a fallback strategy. For URLs without a one-to-one match, find the closest thematic parent or category page. If you had /tutorials/django-rest-api and you're sunsetting Django content, redirect it to /tutorials or your general REST API guide, not the homepage.
For an e-commerce site, if a product is discontinued, redirect the old product page to the category it belonged to, or to a new product that serves the same need. Preserve context.
If a URL truly has no suitable home, use a 410 Gone status instead of a redirect. It tells search engines the page is intentionally gone—not lost, not moved—so stop trying to index it. This is cleaner than a vague redirect.
location = /old-discontinued-feature {
return 410;
}This approach respects both users and search algorithms. Users land somewhere relevant; engines stop wasting crawl budget on deleted content.
Test the map before launch
Testing your redirect map isn't optional. It's the difference between a smooth migration and a ranking collapse that takes months to recover from.
Build a test script that samples redirects at scale. Here's a practical approach:
- Segment your URLs: Divide them into categories (blog, docs, products, etc.). Pick a random sample from each: 10–20% is usually enough if your map is consistent.
- Test each redirect: Make HEAD or GET requests to the old URL, follow redirects, and verify the final destination is correct. Catch loops (A→B→A), chains (A→B→C instead of A→C directly), and broken targets.
- Check status codes: Ensure all redirects return 301 or 302 as intended, not 404 or 500.
- Verify content relevance: Spot-check the target pages. Does a user landing there actually find what they expect?
I built a small Node.js script for a SaaS migration that read our redirect map, tested 500 random redirects, and reported any anomalies. It took an hour to write and saved us from shipping 37 broken chains. Small investment, huge ROI.
const https = require('https');
const testRedirect = (oldUrl, expectedTarget) => {
return new Promise((resolve) => {
https.get(oldUrl, { redirect: 'manual' }, (res) => {
if (res.statusCode === 301 || res.statusCode === 302) {
const location = res.headers.location;
resolve({
old: oldUrl,
target: location,
expected: expectedTarget,
match: location === expectedTarget,
status: res.statusCode
});
} else {
resolve({
old: oldUrl,
status: res.statusCode,
error: 'No redirect found'
});
}
}).on('error', (e) => {
resolve({ old: oldUrl, error: e.message });
});
});
};
// Use this to bulk-test your map
const redirectMap = [
{ old: 'https://old.com/blog/caching', target: 'https://new.com/blog/dotnet-caching' }
];
Promise.all(redirectMap.map(r => testRedirect(r.old, r.target)))
.then(results => console.table(results));Run this test on staging if possible, but also run it once more against production in read-only mode, just before you flip the switch to the new domain. Migrations are high-stress moments; automation catches what humans miss.
Common mistakes
- Missing old URLs because you didn't check logs: Sitemaps are incomplete. Always parse server logs or use Google Search Console to find all pages that got traffic.
- Using relative redirects instead of absolute URLs:
return 301 /new-path;on a multi-domain setup can redirect to the wrong domain. Always use absolute URLs:return 301 https://new-domain.com/new-path;. - Redirecting with query parameters forgotten: If your old URL structure used query strings (e.g.,
?id=123), your redirect must handle or preserve them. Test these explicitly. - Testing only on your local machine: DNS, proxies, CDN configs, and WAF rules can affect redirects in production. Always test against the live domain before going live.
- Forgetting to update internal links: Don't just rely on redirects for internal links. Update them to point to new URLs directly. External links will follow the redirect, but your own links should be fresh from day one.
- Setting redirect timeouts too short: If your old server goes down during the migration window, users and bots can't follow redirects. Keep old infrastructure live for at least 30 days post-migration.
Key takeaways
- Extract URLs from three sources: current sitemaps, web server logs, and analytics. Logs and analytics catch pages sitemaps miss.
- Map one-to-one wherever possible. Direct redirects preserve ranking signals and user context better than generic fallbacks.
- Never default to redirecting everything to the homepage. Use category pages, 410 status codes for intentional deletions, or targeted redirects based on content relevance.
- Test the redirect map at scale before launch. Automate this; use a sampling strategy if your site is huge. Catch loops, broken targets, and status code issues before users see them.
- Document your redirect decisions in a spreadsheet. It clarifies intent, helps stakeholders understand the migration strategy, and protects you when questions arise later.
- Keep both the old and new infrastructure live for 30+ days after the migration. Some users and bots move slowly; patience here prevents a second crisis.






Comments
Loading comments…