Website Migration Without Losing SEO: A Cloudflare Redirects Guide · Daniel Tinizaray

Website Migration Without Losing SEO: A Cloudflare Redirects Guide · Daniel Tinizaray

Migrating a website from one domain to another is one of the most critical moments for a business's SEO. A poorly executed migration can wipe out months or years of organic positioning in days.

This article documents the exact process I followed to migrate a local business site from shopgreenway.com to ecoharbor.com, preserving 100% of organic traffic using 301 redirects on Cloudflare. The names are fictional, but the case and metrics are real.

The Scenario

A business with two physical locations decides to unify its online presence under a single brand. They have:

  • 9 indexed URLs in Google (home, about, contact, blog, 3 articles, category)
  • 2 years of content with stable organic positioning
  • Consistent weekly traffic from local searches
  • A new site with a similar structure but different slugs on some pages

The goal: redirect every old URL to its closest equivalent on the new site, with zero traffic loss. The old domain stays active for 3-4 months while Google processes the migration.

Step 1: URL Inventory (Sitemap Audit)

Everything starts with the sitemap. I downloaded both sitemap.xml files (old and new) and extracted all indexed URLs. Small to medium sites typically have between 5 and 25 URLs that need direct mapping.

⚠️ Golden rule: Don't use a generic catch-all as your only redirect. Every URL deserves a specific destination. A blanket 301 to the home page destroys the topical relevance signal that Google has built for each individual page.

The inventory revealed:

Type Count Example
Main pages 4 /, /about/, /contact/, /blog/
Blog articles 3 /getting-started-organic-products/
Categories / Taxonomies 2 /category/blog/, author archives

Step 2: Mapping Equivalences

Not every route has a 1:1 equivalent between sites. In this case:

  • Home: Direct 1:1 ✅
  • About: Direct 1:1 ✅
  • Contact: Slug changed from /contact-us/ to /contact/ ⚠️
  • Blog: Direct 1:1 ✅
  • 3 articles: No equivalent content on the new site ❌ → redirect to closest blog category
  • Categories: Don't exist as a taxonomy on the new site → redirect to blog archive
📌 Key insight: In real migrations, 20-40% of URLs have no direct equivalent on the destination. Deciding where to send them is the most critical decision in the process. Worst option: home page. Best option: the thematically closest page.

Step 3: Implementation on Cloudflare

Cloudflare provides two main mechanisms for redirects:

Option A: Page Rules (Easy, up to 3 free)

Ideal for small migrations with few routes:

# Page Rule: shopgreenway.com/* →
# Forward URL (301) → ecoharbor.com/

# Exceptions for specific routes via Worker/another rule

Option B: Cloudflare Workers (Recommended for fine control)

For this case, I used a Worker with explicit routing logic. This avoids the catch-all problem that sends everything to the home page:

// Conceptual Worker fragment
const redirects = {
  '/': '/',
  '/about-us/': '/about-us/',
  '/contact-us/': '/contact/',
  '/blogs/': '/blogs/',
  '/category/blog/': '/blogs/',
};

// Articles without equivalents → thematic blog category
const blogPatterns = [
  { pattern: '/getting-started/', target: '/blogs/category-101/' },
  { pattern: '/oil-benefits/', target: '/blogs/category-101/' },
  { pattern: '/beginners-guide/', target: '/blogs/category-101/' },
];

async function handleRequest(request) {
  const url = new URL(request.url);
  const path = url.pathname;

  if (redirects[path]) {
    return Response.redirect(
      'https://ecoharbor.com' + redirects[path], 301
    );
  }

  for (const { pattern, target } of blogPatterns) {
    if (path.startsWith(pattern)) {
      return Response.redirect(
        'https://ecoharbor.com' + target, 301
      );
    }
  }

  // Fallback: home
  return Response.redirect('https://ecoharbor.com/', 301);
}

Step 4: Rigorous Validation

Once redirects were implemented, I validated every single one from the terminal:

for url in \
  "https://shopgreenway.com/" \
  "https://shopgreenway.com/about-us/" \
  "https://shopgreenway.com/contact-us/"; do
  curl -sI -o /dev/null -w "%{http_code} %{redirect_url}" "$url"
  echo
done

This script revealed 3 bugs in my first iteration:

  • The catch-all was overwriting the /about-us/ redirect (went to home instead of about)
  • /blogs/ was also falling into the generic catch-all
  • /category/blog/ had the same issue — I fixed all three by placing specific rules before the catch-all
🔍 The lesson: Don't trust theory. Always validate every redirect with curl or a similar tool. In website migrations, details matter. A single misdirected route can cost dozens of organic visits per week to a page with valuable content.

Results

After corrections, all 8 redirects were 100% validated:

Source Destination Status
shopgreenway.com/ ecoharbor.com/ 301 ✅
shopgreenway.com/about-us/ ecoharbor.com/about-us/ 301 ✅
shopgreenway.com/contact-us/ ecoharbor.com/contact/ 301 ✅
shopgreenway.com/blogs/ ecoharbor.com/blogs/ 301 ✅
shopgreenway.com/3-articles/ ecoharbor.com/blogs/category-101/ 301 ✅
shopgreenway.com/category/blog/ ecoharbor.com/blogs/ 301 ✅

Lessons Learned

  1. Always start from the sitemap, not from what you think exists. The old site's sitemap.xml is your source of truth. If it's not there, it's not indexed.
  2. Don't use a catch-all without explicit exceptions. A generic /* → / works, but only if specific rules are evaluated first.
  3. Validate with curl, not the browser. Browsers hide redirects. Curl gives you the exact status code and destination URL.
  4. Keep the old domain active for 3-4 months. Google needs time to process the 301s, update its index, and transfer link juice. Turning off the old domain early is mistake #1 in migrations.
  5. Document the mapping. When someone asks in 3 months "where does route X redirect to?", having a mapping document or a comment in your task management tool saves hours of debugging.

Bonus: Post-Migration Monitoring

After the migration, I monitored three things for 2 weeks:

  • Google Search Console: Verify the new domain is being indexed and the old one shows the expected decline in indexed URLs
  • UptimeRobot: Confirm all destination endpoints return 200, not 404
  • Analytics: Compare weekly organic traffic pre vs post migration. A 10-15% dip in the first few days is normal while Google processes the 301s
💡 If traffic doesn't recover within 4 weeks: Check for redirect chains (301 → 302 → 301), whether the new site has the same optimized content, and whether sitemaps are correctly submitted to Search Console. 90% of post-migration issues are resolved with a sitemap audit.

Conclusion

Domain migration doesn't have to be traumatic for SEO. The process is simple in theory (map + redirect + validate), but details matter. A misprioritized catch-all, a slug that changed without notice, a forgotten category route — every small error compounds into lost traffic.

Cloudflare simplifies the technical implementation, but the real work is in the upfront analysis: understanding what URLs exist, where they should point, and validating that they actually point where you said they would.


Enjoyed this article?

If you're dealing with these challenges in your company, let's talk. No obligation. 30 minutes to understand your situation.

Book a free call →