Migrating WordPress to Astro: A Case Study with 6× Speedup
Step-by-step migration of a WordPress blog with hundreds of posts to Astro — plus redirects, images, and SEO tips often missed.
Why migrate?
WordPress is fine, but:
- Slow performance — average LCP 3-6s vs Astro’s 1-2s
- Expensive hosting — needs PHP + MySQL + a cache layer; Astro is static (free on Cloudflare Pages)
- Heavy maintenance — PHP, plugin, and security updates weekly
- Bloat — every plugin injects JS, often unused
Step-by-step migration
1. Export content from WordPress
Use WP All Export or the REST API:
curl https://oldsite.com/wp-json/wp/v2/posts?per_page=100&page=1 > posts.json
Loop pages until done.
2. Convert to Markdown
Tools: wordpress-export-to-markdown (npm). Or a small Node script:
import TurndownService from 'turndown';
const td = new TurndownService();
const md = td.turndown(post.content.rendered);
fs.writeFileSync(`src/content/blog/${post.slug}.md`,
`---
title: "${post.title.rendered}"
date: ${post.date}
slug: ${post.slug}
---
${md}`);
3. Set up an Astro content collection
// src/content.config.ts
import { defineCollection, z } from "astro:content";
import { glob } from "astro/loaders";
export const collections = {
blog: defineCollection({
loader: glob({ pattern: "**/*.md", base: "./src/content/blog" }),
schema: z.object({
title: z.string(),
date: z.coerce.date(),
slug: z.string(),
}),
}),
};
4. Download & re-host images
WP stores images in /wp-content/uploads/. Download them to public/images/ and update markdown paths.
Tip: serve as AVIF/WebP instead of original JPG/PNG. Sharp + Astro <Image> handle this automatically at build.
5. Redirects from the old URLs
Astro permalinks may differ from WP. Create a _redirects file (Cloudflare/Netlify):
/2024/06/old-post-slug /blog/old-post-slug 301
/category/foo /blog/tag/foo 301
/wp-admin/* / 302
Or in nginx/Caddy:
redir /2024/* /blog{path} permanent
6. Preserve SEO
Migrate:
- Meta title & description per post
- OG image (or generate fresh from a template)
- Canonical URL pointing at the new domain
- Sitemap — Astro auto-generates
- Structured data (Article schema)
- Hreflang if multi-language
7. Submit changes to Google
- Google Search Console → Add new property
- Submit sitemap:
https://newsite.com/sitemap.xml - URL Inspection for the top 10 URLs — request indexing
- Monitor the “Coverage” report for 2-4 weeks
Real-project results
A retail client: WordPress blog, 280 posts, 50K/month traffic.
| Metric | WordPress | Astro |
|---|---|---|
| LCP (mobile 4G) | 4.8s | 0.9s |
| Lighthouse score | 42 | 98 |
| Build time | n/a | 18s |
| Hosting cost | $40/month | $0 (Cloudflare Pages) |
| Maintenance hrs/month | ~6 | ~0.5 |
Organic traffic usually improves after a migration thanks to a stronger SEO and performance foundation.
Common pitfalls
- Missing redirects — rankings drop instantly when URLs change without 301s.
- Broken image paths — WP relative paths differ from Astro’s.
- Form submissions — WP has Contact Form 7; Astro static needs external (Formspree, Netlify Forms) or your own backend.
- Comment systems — Disqus as a drop-in, or Giscus for dev blogs.
- WP shortcodes — Markdown has no equivalent; convert manually or write custom MDX components.
WordPress migration consult — we’ve migrated 30+ WP sites to Astro/Next.js.