Recovering Search Traffic Lost After a Generator Migration

The migration went well. The new Astro site was faster, the build was a third of the time, and the content parity audit passed. Three weeks later, organic clicks were down 27% and still falling. This is a familiar story: generator migrations change far more than the generator — URLs, redirects, canonical tags, titles, structured data, internal links, pagination — and search engines notice every one. Most of the changes are harmless. A few are not, and they hide among thousands of pages.

This guide works through the recovery for a 3,100-page developer blog and documentation site migrated from Gatsby to Astro. It shows how to find which pages lost traffic, the five causes found, the fixes, and how traffic came back. It belongs to Migrating Between Static Site Generators.

Step 1: Confirm It Is the Migration

Before investigating the site, rule out the outside world. Three checks:

  • Timing. The decline began four days after the launch, matching recrawl timing, not a known search engine update date.
  • Scope. Branded queries were flat; non-branded queries fell. Other sites in the same niche, checked in a third-party visibility tool, were flat.
  • Channel. Direct and referral traffic were unchanged, so it was not a tracking problem — a frequent false alarm when a migration also changes the analytics snippet.

Step 2: Find the Pages That Lost Traffic

Search Console's page report for four weeks before and four weeks after, joined through the redirect map so old and new URLs line up:

import pandas as pd
before = pd.read_csv('gsc-pages-before.csv')            # page, clicks
after = pd.read_csv('gsc-pages-after.csv')
redirects = pd.read_csv('redirects.csv')               # old, new
before['page'] = before['page'].map(dict(zip(redirects.old, redirects.new))).fillna(before['page'])
df = before.merge(after, on='page', how='outer', suffixes=('_b', '_a')).fillna(0)
df['loss'] = df.clicks_b - df.clicks_a
df['section'] = df.page.str.extract(r'^https://[^/]+/([^/]+)/')
print(df.groupby('section').loss.sum().sort_values(ascending=False))

The loss was concentrated: 210 pages accounted for 81% of lost clicks, and they grouped into five patterns.

Share of lost organic clicks attributed to each cause Horizontal bars. Missing redirects for paginated and tag URLs 34 percent. Canonical tags pointing at URLs without a trailing slash 22 percent. FAQ and HowTo structured data removed 15 percent. Titles rewritten by the new template 11 percent. Code examples rendered only on the client 7 percent. Unexplained 11 percent. Lost clicks by cause Missing redirects 34% Canonical mismatch 22% Structured data dropped 15% Titles changed 11% Client-only code samples 7% Unexplained 11%
Lost clicks over four weeks, after mapping old URLs to new through the redirect file.

Cause 1: Redirects Missing for URLs Nobody Listed

The redirect map covered every content page — it had been generated from the old site's page list. It did not cover URLs Gatsby generated that were not pages in the content: tag pages (/tags/react/), pagination (/blog/page/7/), and a legacy /posts/:slug pattern from an even older site that Gatsby had redirected with a plugin. 610 URLs now returned 404, including tag pages that ranked for broad topic queries.

The fix came from the old site's access logs rather than its content: every URL that had received a search referral in the previous six months, compared against the new site's routes. Missing ones got redirects — tag pages to the new tag pages (the slugs had changed), pagination to the first page of the section, and the legacy pattern to the posts. Keeping Redirects Working After an SSG Migration covers building that map.

Cause 2: Canonicals That Disagreed With the URL

Astro was configured with trailingSlash: 'always', and pages were served at /blog/post/. The SEO component, ported from Gatsby, built the canonical as site + pathname with the pathname normalised without a trailing slash. Every page declared a canonical URL that redirected to itself plus a slash. Search engines saw thousands of pages saying "the real version is elsewhere" and pointing at a redirect, and in many cases chose another URL — sometimes the old Gatsby URL, still cached — as canonical.

The fix was one line in the component (new URL(Astro.url.pathname, Astro.site)), plus a CI test asserting that every page's canonical equals its own URL, as in Testing Structured Data and Meta Tags.

Cause 3: Structured Data That Did Not Make the Trip

The Gatsby site had emitted FAQ and HowTo JSON-LD on tutorial pages through a plugin that read front matter. Nobody had listed it in the migration inventory because it was invisible on the page. Its loss removed rich results from 140 tutorial pages, which cut their click-through rate even where rankings held. Porting it as an Astro component reading the same front matter restored it; Search Console's rich results report confirmed recovery within ten days.

Visible page features against invisible ones in the migration inventory Two panels. The inventory covered visible things, content, images, navigation, code blocks and comments. It missed invisible things, tag and pagination URLs, the legacy redirect plugin, canonical URL format, JSON-LD from front matter, and title templates. What the migration inventory missed In the inventory content and images navigation and footer code blocks and embeds comments widget all ported correctly Not in the inventory tag and page URLs legacy redirect plugin canonical format JSON-LD and titles caused the traffic loss
Every cause of the traffic drop was on the right-hand side.

Cause 4: Titles Rewritten by the New Template

The new layout built titles as {page title} | Acme Docs. The old site had used the front matter seoTitle field when present — set by hand on the 200 highest-traffic pages, often longer and more specific than the display title. The new template ignored it. Restoring seoTitle ?? title brought back the hand-tuned titles; a diff of <title> across old and new builds showed 214 pages had changed, and all but nine went back to the old wording.

Cause 5: Content That Moved to the Client

Interactive code examples on 60 pages had been server-rendered in Gatsby (as part of React's static render) and hydrated afterwards. In Astro, the team had made them client:only islands to avoid an SSR incompatibility in the editor library. The code no longer appeared in the HTML at all. Search engines render JavaScript, but later and less reliably, and the pages lost rankings for queries matching the code. Rendering the code as a static <pre> block with the interactive editor loaded over it as an island restored the content to the HTML.

Step 3: Submit and Monitor

After the fixes deployed, the updated sitemap was resubmitted and indexing was requested for the 50 highest-loss URLs. Daily monitoring tracked clicks, indexed pages, and 404s from search referrals in the edge logs.

Organic clicks per week as a percentage of the pre-migration baseline Vertical bars. Week of migration 100 percent. Three weeks after migration 73 percent, when fixes were deployed. One week after fixes 81 percent. Two weeks 88 percent. Three weeks 93 percent. Five weeks 98 percent. Weekly clicks relative to the pre-migration baseline 100% Migration week 73% +3 wk (fixes) 81% +1 wk after 88% +2 wk 93% +3 wk 98% +5 wk
Search Console clicks, seven-day sums, baseline is the four-week average before the migration.

Preventing It Next Time

A pre-launch check list now runs against the new build and the old one side by side:

  1. Every URL with search traffic in the last six months returns 200 or a single 301.
  2. Every page's canonical equals its own final URL.
  3. <title>, meta description and JSON-LD types match the old site per URL, or differ intentionally.
  4. The main content text of each page is present in the HTML without JavaScript.

The script that does this is about 150 lines and runs in 3 minutes over 3,100 pages, and it would have caught all five causes before launch. Auditing Content Parity After a Migration covers the content half of the same idea.

Measured Impact

Measure3 weeks after migration5 weeks after fixesTool
Organic clicks vs baseline73%98%Search Console
404s from search referrals / week4,100120Edge logs
Pages with self-referencing canonical0%100%CI test
Pages with rich results12151Search Console
Titles matching old tuned titles0 of 205196 of 205<title> diff

Pitfalls & Rollback

  • Building the redirect map from content files only. Use logs and search data to find every URL that matters.
  • Porting the SEO component without testing its output. Compare canonicals, titles and JSON-LD per URL.
  • Moving content to client-only islands. Keep text in the HTML.
  • Rolling back in a panic. It restarts the recrawl; fix forward.
  • Rollback: keep the old build for comparison; each fix above is a small, independent change.

Conclusion

Traffic lost after a generator migration comes from what the migration did not list: URLs that were never content pages, canonical formats, structured data, hand-tuned titles and server-rendered content that moved to the client. Finding the pages that lost clicks, grouping them, and fixing each cause brought this site from 73% to 98% of its baseline in five weeks — and a side-by-side pre-launch check now catches all five before launch.

FAQ

Is a traffic drop after a migration normal?

A small dip of a few percent for two to four weeks is common while search engines recrawl and reprocess changed pages. A drop larger than about 10 percent, or one that is still growing after two weeks, almost always has a concrete cause such as missing redirects, changed canonical URLs, lost structured data or pages that no longer render their content in HTML.

How do I find which pages lost traffic?

Export Search Console page-level clicks for equal periods before and after the migration, join them on URL after mapping old URLs to new ones with the redirect map, and sort by absolute loss. A small number of pages usually accounts for most of the loss, and grouping them by template or section points at the cause.

How long does recovery take after fixing the cause?

For redirect and canonical fixes, search engines typically pick up changes within days for frequently crawled pages and a few weeks for the rest. Submitting updated sitemaps and requesting indexing for the most important pages speeds up the first part. In the case in this guide, clicks returned to 98 percent of baseline five weeks after the fixes.

Should I roll back the migration if traffic drops?

Rarely. Rolling back changes URLs and markup a second time and starts the recrawl over. Unless the new site is fundamentally broken, it is faster to find and fix the specific causes. Keep the old build available for comparison rather than for redeployment.

Filed underFixes