Skip to main content
Marketing Agencies|SEO Specialist / Web Developer

Website Migration SEO Validation

Verify SEO elements are preserved during site migrations

Website migrations often damage search rankings when SEO elements are misconfigured. EdgeDNS validates critical SEO infrastructure — hreflang reciprocity for international sites, per-crawler robots rules, sitemap URL sampling, and canonical drift — to catch issues before they impact traffic.

The Challenge

Website migrations and platform changes frequently cause SEO disasters. Redirects break, canonical tags point to wrong URLs, hreflang alternates lose their reciprocal return links, sitemaps reference old pages, and robots.txt drifts between Googlebot and AI crawlers. These issues can take months to recover from and cause significant traffic and revenue loss.

The Solution

Run automated SEO validation against your staging environment before migration. Audit hreflang reciprocity (Developer+) so every international alternate has a return link. Sample-validate sitemap URLs (Pro+) to confirm they return 200 with accurate `lastmod`. Resolve the effective robots rule for every major crawler (Googlebot, GPTBot, ClaudeBot, and 10 more) so you know what each one actually sees. Compare pre- and post-migration scans to catch every regression.

Endpoints Used

Combine these EdgeDNS endpoints to build this solution.

GET
/v1/domain/canonicalTry in Playground

Canonical URL: Verify canonical tags and audit hreflang reciprocity — every alternate must have a return link (Developer+)

GET
/v1/domain/metaTry in Playground

Meta Tags: Check title, description, OG tags, parsed robots directives, verification tags, and platform-specific meta

GET
/v1/domain/sitemapTry in Playground

Sitemap: Verify sitemap accessibility, sample-validate listed URLs (Pro+), and check `lastmod` accuracy against actual page changes

GET
/v1/domain/robotsTry in Playground

Robots.txt: Resolve effective rules per crawler (13 crawlers including GPTBot, ClaudeBot, CCBot) and HEAD-verify referenced sitemaps

GET
/v1/domain/redirectTry in Playground

Redirect Chain: Trace every hop after migration with category labels (protocol-upgrade, canonical, mixed-content, open-redirect-suspect, meta-refresh); pass `canonicalCheck=true` to verify the chain lands on a URL whose `<link rel=canonical>` matches

GET
/v1/domain/response-timeTry in Playground

Response Time: Verify site performance post-migration with p90/p95/p99 TTFB and Server-Timing breakdown

Results You Can Achieve

Pre-flight migration validation in seconds

Catch redirect chains, missing canonicals, hreflang return-link gaps, and broken sitemap URLs before search engines crawl the new domain.

Redirect-chain SEO analysis

Hop-by-hop category labels distinguish desired redirects (HTTP→HTTPS, www↔non-www) from problems (302 used for permanent moves, mixed-content, canonical mismatches). The `canonicalCheck` parameter catches "you redirect to /foo but /foo's canonical is /bar" — a silent ranking leak after migrations.

Hreflang reciprocity audit for international sites

Every hreflang alternate fetched and checked for a return link. Missing return links are the #1 silent SEO disaster on global migrations — caught automatically (Developer+).

Per-crawler robots resolution + sitemap URL sampling

See exactly which rule Googlebot, GPTBot, and ClaudeBot apply post-launch. Sample-validate sitemap URLs to confirm they return 200 with accurate `lastmod` (Pro+).

Before/after diff per migrated URL

Side-by-side scan output documents which signals improved, which regressed, and which need follow-up — replaces ad-hoc spreadsheet tracking.

Migration audit log dated for stakeholders

A scored, timestamped run is shareable with marketing and SEO for go/no-go sign-off.

Code Example

SEO validation for website migration

javascript
async function validateMigrationSEO(urls) {
  const headers = { 'Authorization': 'Bearer YOUR_API_KEY' };
  const issues = [];

  for (const url of urls) {
    const domain = new URL(url).hostname;

    const [canonical, meta, robots, redirect] = await Promise.all([
      // validateReciprocity audits hreflang return-links (Developer+ tier)
      fetch(`https://api.edgedns.dev/v1/domain/canonical?url=${url}&validateReciprocity=true`, { headers }),
      fetch(`https://api.edgedns.dev/v1/domain/meta?url=${url}`, { headers }),
      fetch(`https://api.edgedns.dev/v1/domain/robots?domain=${domain}`, { headers }),
      // canonicalCheck=true compares the final URL to its <link rel=canonical>
      fetch(`https://api.edgedns.dev/v1/domain/redirect?url=${url}&canonicalCheck=true`, { headers }),
    ].map(p => p.then(r => r.json())));

    if (canonical.data.canonical && canonical.data.canonical !== url) {
      issues.push({ url, issue: `Canonical mismatch: ${canonical.data.canonical}` });
    }

    // Hreflang reciprocity: every alternate must link back
    const missing = canonical.data.hreflangReciprocity?.missingReturnLinks ?? [];
    for (const m of missing) {
      issues.push({ url, issue: `Hreflang ${m.lang} (${m.href}) has no return link` });
    }

    if (!meta.data.title) issues.push({ url, issue: 'Missing title tag' });
    if (!meta.data.description) issues.push({ url, issue: 'Missing meta description' });

    // Redirect-chain checks: chain length, 302-for-permanent, canonical drift
    if (redirect.data.totalRedirects > 2) {
      issues.push({ url, issue: `Redirect chain has ${redirect.data.totalRedirects} hops — consolidate to ≤ 1` });
    }
    for (const issue of redirect.data.structuredIssues ?? []) {
      if (issue.code === 'redirect-temporary-for-permanent') {
        issues.push({ url, issue: '302/307 used for what looks like a permanent move — switch to 301 for ranking signals' });
      }
      if (issue.code === 'redirect-canonical-mismatch') {
        issues.push({ url, issue: `Redirect lands on ${redirect.data.finalUrl} but its canonical is ${redirect.data.canonicalMatch?.canonicalUrl}` });
      }
    }

    // Verify Googlebot, GPTBot, and ClaudeBot are not accidentally blocked
    const rules = robots.data.effectiveRulesByCrawler ?? {};
    for (const bot of ['Googlebot', 'GPTBot', 'ClaudeBot']) {
      if (rules[bot]?.effective === 'disallow-all') {
        issues.push({ url, issue: `${bot} is disallow-all in robots.txt` });
      }
    }
  }

  // validateUrls samples and HEAD-checks each listed sitemap URL (Pro+ tier)
  const domain = new URL(urls[0]).hostname;
  const sitemapCheck = await fetch(
    `https://api.edgedns.dev/v1/domain/sitemap?domain=${domain}&validateUrls=true`,
    { headers }
  ).then(r => r.json());

  if (!sitemapCheck.data.accessible) {
    issues.push({ url: 'sitemap.xml', issue: 'Sitemap not accessible' });
  }
  const brokenSample = sitemapCheck.data.sampleValidation?.broken ?? [];
  for (const b of brokenSample) {
    issues.push({ url: b.url, issue: `Sitemap URL returned ${b.status}` });
  }

  return { passed: issues.length === 0, issues };
}

Learn More

Explore industry standards and best practices related to this use case.

Ready to build Website Migration SEO Validation?

Get started with 200 free API requests per month. No credit card required.

Other Use Cases