Back to blog
Tutorial

WordPress to Astro: how to migrate without losing SEO

By Flávio Emanuel · · 9 min read

Why migrate

WordPress works. Millions of sites run on it and don’t need to change. Migration makes sense when the site is basically static content (institutional, LP, portfolio, blog) and performance is suffering because of the stack.

The typical case: site with a purchased theme, 8-12 active plugins, Lighthouse between 50 and 70, LCP above 3 seconds. The site owner pays R$50-100/month for shared hosting and spends hours each month updating plugins to avoid vulnerabilities. The site does what a static HTML page would do, but with 10x more complexity.

With Family Pilates, that was exactly it. WordPress with a purchased theme, Lighthouse 65, LCP of 3.2 seconds. After migrating to Astro with Vercel deployment: Lighthouse 98, LCP 0.8s, free hosting, zero plugins to maintain.

Migration isn’t mandatory. It’s a technical decision that makes sense when performance, maintenance costs, and technical SEO are priorities. If the site works well on WordPress and the client is satisfied, there’s no reason to change.

Before starting: inventory what exists

The migration that goes wrong is the one that starts with code without mapping what already exists. Before touching any file, I do a complete inventory:

Existing URLs. List all current site URLs. Each one needs to keep working or have a redirect. A URL that returns 404 after migration is lost Google ranking.

The fastest way: export the WordPress sitemap.xml (usually at yoursite.com/sitemap.xml). If there’s no sitemap, use Screaming Frog (free version up to 500 URLs) to crawl all pages.

Current meta tags. Note the title and description of each page. If they’re well configured (keyword in title, description between 120-160 chars), keep them identical in Astro. If they’re generic, use the migration as an opportunity to optimize.

Schema.org. Check if the current site has structured data. Most WordPress sites don’t, or have the basics that Yoast generates automatically. Either way, note what exists so you don’t lose it.

Content. Text, images, documents. Copy everything. Images need re-optimization anyway (WebP, correct dimensions), so it’s a good time to do it.

Functionality. What does the site do besides display content? Contact form? Scheduling? Blog with comments? Each feature needs an equivalent in Astro or a conscious decision to remove it.

URL structure: don’t change if you don’t need to

The most important migration rule: keep the same URLs. If the services page was yoursite.com/services, it stays yoursite.com/services in Astro.

In Astro, the folder structure inside src/pages/ defines URLs:

src/pages/
  index.astro          → yoursite.com/
  services.astro       → yoursite.com/services/
  about.astro          → yoursite.com/about/
  contact.astro        → yoursite.com/contact/
  blog/
    [slug].astro       → yoursite.com/blog/post-name/

If WordPress used a different structure (e.g., yoursite.com/?p=123 or yoursite.com/2024/01/post-name), you need redirects.

Redirects: what saves your SEO

Every old URL that changes needs a 301 redirect (permanent). The redirect tells Google: “this page moved address, transfer the authority to the new URL.”

On Vercel, configure in vercel.json:

{
  "redirects": [
    { "source": "/2024/01/post-name", "destination": "/blog/post-name", "permanent": true },
    { "source": "/?p=123", "destination": "/services", "permanent": true }
  ]
}

On Netlify, use the _redirects file:

/2024/01/post-name  /blog/post-name  301
/?p=123  /services  301

Don’t skip this step. A URL without a redirect returns 404. Google indexes the 404, removes the old page from the index, and you lose the accumulated authority. Depending on organic traffic volume, the impact can be significant.

After configuring redirects, test them all. Open each old URL in the browser and confirm it redirects to the new URL. Tools like Screaming Frog do this automatically for large lists.

Migrating the content

Text

Copy WordPress content and convert to Markdown or directly to Astro components. If the site has a blog, Astro works natively with .md files in the src/content/ folder.

For institutional pages (home, about, services), content goes directly into .astro files. No database, no CMS, no admin panel. The text lives in the code. For small sites (5-15 pages), this is an advantage: less complexity, fewer failure points.

If the client needs to edit content without touching code, consider a headless CMS (Decap, Tina, Contentful). But in my experience, most institutional sites don’t need frequent editing. The client changes the site 2-3 times a year. For that, a one-time delivery works better than a CMS.

Images

Don’t copy images in their original format. This is the time to optimize everything:

  • Convert to WebP (squoosh.app or bulk converter)
  • Resize to 2x display resolution (800px layout image becomes 1600px)
  • Use Astro’s <Image> component that optimizes automatically at build time
  • Add descriptive alt text (80-125 characters, with keyword when natural)

In WordPress, images are usually in /wp-content/uploads/. Download all, optimize, and place in src/assets/ or public/images/ in the Astro project.

I detailed the complete image optimization process in the post about why your website loads slowly.

Forms

WordPress uses Contact Form 7, Gravity Forms, or WPForms. In Astro, static forms can submit to services like Formspree, Netlify Forms, or a custom endpoint.

For clinics and local businesses, I usually replace the form with a direct WhatsApp button. Conversion rate is higher and response time is faster. If the form is essential (e.g., lead capture with email), Formspree integrates in 5 minutes.

Technical SEO: what to configure in Astro

Meta tags

In Astro, meta tags go in the <head> of the main layout. Create a reusable SEO.astro component:

---
const { title, description, image, url } = Astro.props;
---
<title>{title} | Company Name</title>
<meta name="description" content={description} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:image" content={image} />
<meta property="og:url" content={url} />
<link rel="canonical" href={url} />

Each page passes its own values. No plugin, no complex configuration. One function doing the work that in WordPress requires Yoast or RankMath.

Schema.org

JSON-LD directly in the <head>. No plugin. The Schema I recommend for each page type:

  • Home: Organization or LocalBusiness
  • Blog: BlogPosting with author, datePublished, dateModified
  • FAQ: FAQPage
  • Navigation: BreadcrumbList

Implementing each takes minutes. The impact on rich snippets can be significant. I detailed the full implementation in the post about technical SEO for developers.

Sitemap

Install @astrojs/sitemap and configure in astro.config.mjs:

import sitemap from '@astrojs/sitemap';

export default defineConfig({
  site: 'https://yoursite.com',
  integrations: [sitemap()],
});

The sitemap is generated automatically at build time. Register it in Google Search Console after deployment.

Fonts: stop depending on Google Fonts

WordPress loads Google Fonts with external requests. This adds latency and blocks text rendering.

In Astro, do self-hosting:

  • Download fonts in woff2
  • Place in public/fonts/
  • Declare with @font-face in global CSS
  • Use font-display: swap and preload in <head>

This process is standard across all my projects. With Family Pilates I use Fraunces and Satoshi self-hosted. With Soline, custom fonts served from the same server. Zero external requests for typography.

Deploy and DNS

With the Astro site ready, deploying to Vercel takes 5 minutes:

  1. Push the code to GitHub
  2. Connect the repository on Vercel
  3. Vercel detects Astro automatically and builds
  4. Configure the custom domain in the Vercel dashboard
  5. Point the domain DNS to Vercel (CNAME or A record)

HTTPS is automatic. Global CDN is automatic. No server to configure, no certificate to renew.

Hosting that cost R$50-100/month on WordPress now costs R$0 on Vercel (free plan). The site loads faster, is more secure (no exposed admin panel), and requires no plugin maintenance.

After deployment: validation

In the first 7 days after migration, monitor:

  • Google Search Console: crawl errors, unindexed pages, 404s
  • Test all redirects manually
  • Compare Lighthouse before and after (WordPress screenshot vs Astro)
  • Verify the new sitemap was accepted by Google
  • Monitor organic traffic (temporary 5-10% drop is normal in the first 2 weeks)

Temporary organic traffic drop happens in every migration. Google needs to crawl the new pages, process the redirects, and recalculate positions. In 2-4 weeks, traffic returns to normal. If redirects are correct and meta tags are maintained, it usually comes back above previous levels because of the performance improvement.

With GPM2 and Tok Final, both built in Astro from the start, Lighthouse hits 95+ and technical SEO comes configured out of the box. For sites migrated from WordPress, the performance jump is even more visible.

Migration checklist

  • Complete inventory of URLs, meta tags, and content
  • 301 redirects for all changed URLs
  • Meta tags identical to or better than previous ones
  • Schema.org configured (LocalBusiness, BreadcrumbList, FAQPage)
  • Sitemap.xml generated and registered in Search Console
  • Images converted to WebP with alt text
  • Self-hosted fonts with font-display: swap
  • Forms replaced or migrated
  • Lighthouse above 90
  • DNS pointing to new host (Vercel/Netlify)
  • Monitoring for 7 days post-deployment
Next step

Need a dev who truly delivers?

Whether it's a one-time project, team reinforcement, or a long-term partnership. Let's talk.

Chat on WhatsApp

I reply within 2 hours during business hours.