When I pull a client off WordPress, Yoast (or Rank Math) is usually one of the first plugins they ask about. They spent years tweaking titles and meta descriptions. They dont want that work thrown out with the old theme.
Here is the checklist I actually run so rankings dont fall off a cliff during the move.
1. Export the SEO data before you touch the theme
I never migrate "by feel". I pull a CSV of every public URL with:
- post/page title
- Yoast SEO title (or Rank Math equivalent)
- meta description
- canonical if they set one
- noindex / nofollow flags
- focus keyphrase (nice to have, not required)
If a plugin export is messy I just query wp_postmeta for _yoast_wpseo_title and _yoast_wpseo_metadesc and join it to the posts table. Ugly SQL, works every time.
Save that file. It becomes your source of truth for the Astro site.
2. Put titles and descriptions next to the content
On Astro I keep SEO fields in frontmatter (or a seo object on each content entry), not buried in a layout:
---
title: "Emergency plumber in Surry Hills"
description: "Same-day plumbing in Surry Hills. Fixed pricing, no call-out fee."
canonical: "/emergency-plumber-surry-hills/"
noindex: false
---
For pages that used to live in WordPress custom fields or ACF, I put the same shape in /data/*.json so the client can still tweak copy later through the /edit flow without opening a repo.
One rule: the Astro title in frontmatter is the browser title. If Yoast used a seperate SEO title template, map that into seoTitle and prefer it in the layout. Dont silently overwrite the H1.
3. One layout owns the head tags
I wire a single BaseLayout.astro (or equivalent) that always emits:
<title><meta name="description">- Open Graph title, description, image, url
- Twitter card tags
- canonical link
- robots when
noindexis true
Something like:
---
const pageTitle = Astro.props.seoTitle || Astro.props.title;
const desc = Astro.props.description || "";
const canonical = new URL(Astro.props.canonical || Astro.url.pathname, Astro.site);
---
<title>{pageTitle}</title>
<meta name="description" content={desc} />
<link rel="canonical" href={canonical} />
<meta property="og:title" content={pageTitle} />
<meta property="og:description" content={desc} />
<meta property="og:url" content={canonical} />
Keep OG image defaults sane. A lot of small business sites had Yoast pulling a random featured image. I set one default brand OG image and only override when the page has a real hero.
4. Match canonicals to your redirect map
This is where people lose rankings. If /services/plumbing/ used to be the URL and your Astro route is /plumbing/, the redirect map and the canonical must agree.
I build the redirect CSV first (old URL → new URL), then set each page's canonical to the new URL. Old URLs 301. New URLs self-canonical. No surprises.
If Yoast had odd trailing-slash rules, pick one convention in Astro and stick to it everywhere (including the host). I usually keep trailing slashes for marketing sites so WordPress habits don't fight me.
5. Sitemap and robots without a plugin
Drop @astrojs/sitemap into the project and set site in astro.config. That covers most of what Yoast's XML sitemap did.
For robots.txt I keep it boring:
User-agent: *
Allow: /
Sitemap: https://client-domain.com/sitemap-index.xml
Only add Disallow rules you can explain out loud. I have seen clients with Yoast blocking half the site by accident. Dont copy that blindly.
6. Structured data: only what you need
I do not port every Yoast schema graph on day one. For a typical local service site I add:
- Organization (or LocalBusiness) on the homepage
- WebPage / Article on blog posts if they have a blog
- BreadcrumbList when the nav is nested
Plain JSON-LD in the layout is fine. Fancy schema plugins can wait until Search Console is clean.
7. Preview check before DNS moves
On the preview deploy I spot-check 10–20 money pages:
- View source: title + description match the CSV
- Canonical points at the final public URL shape (even if preview host differs, the path should be right)
- No accidental
noindexleft on from staging - Sitemap lists those URLs
- Old URLs 301 on the preview host if you already shipped
/_redirects
I deploy the preview with npx novence deploy so the client can click real URLs on novence.ai before we touch DNS. Same build that will go live.
Then I crawl the preview with a quick link checker and grep the HTML for empty descriptions. Empty meta descriptions are the silent killer after a migration.
8. After cutover
Once DNS is live:
- Submit the new sitemap using the Search Console checklist
- Use URL Inspection on the top 5 money pages
- Watch Coverage / Page indexing for a week
- Keep the WordPress host up (read-only is fine) until the important URLs show as indexed on the new host
If a page drops, it is almost always a bad redirect or a canonical pointing at a soft 404, not "Astro is bad for SEO".
What I tell the client
You are not losing Yoast. You are moving the same titles and descriptions into the site itself, where they ship with every deploy and dont depend on a PHP plugin staying updated.
That conversation lands better than "we deleted your SEO plugin".
If you are mid-migration and stuck on a weird Yoast template string (%%title%% %%sep%% %%sitename%%), map the tokens once in a small script and write the resolved strings into frontmatter. Do it once. Dont leave template tokens in production HTML.
Top comments (3)
The redirect-map-first point is the one I'd underline — canonicals and redirects disagreeing is exactly how a clean migration still drops rankings.
One addition: run the same CSV diff after cutover, not just on preview, since staging noindex flags and trailing-slash defaults are the two things I've seen slip through at the last step.
This is the checklist I wish more moves followed — exporting the SEO data first is the whole game. One thing I'd add for small local sites: don't just spot-check the money pages, pull the top 20 pages from Search Console and analytics first, because a blog post or FAQ ranking for a local query is often the real entry point. And keep that CSV with the project after cutover, not just during the move — six months later it's the only record of what the old titles were and why a redirect exists.
SEO 제목과 H1을 조용히 덮어쓰지 않고 따로 매핑하는 기준이 좋네요. 리디렉션 표와 canonical을 함께 대조한다는 단계도 이전 데이터 보존 목적에 잘 맞습니다. 표본 페이지를 고를 때 방문량이 높은 주소뿐 아니라 수동 canonical·noindex가 있던 예외 주소도 한 건씩 포함하면 이관 누락을 찾기 쉬울 것 같습니다.