How I Migrated My WordPress Site to Astro and MDX
A practical guide on migrating from WordPress to Astro with MDX. Learn how I moved wpdoze.com to bitdoze.com using wordpress-export-to-markdown, Codex CLI, and image optimization.
I had a WordPress blog at wpdoze.com with a bunch of old articles that I wanted to move over to bitdoze.com, which runs on Astro with MDX. I kept putting it off because I figured it would be painful. Turns out it was not that bad, and I want to share what I did so you can skip the trial-and-error part.
Why I left WordPress
Look, WordPress works. Millions of sites run on it for good reason. But my site was just static articles, and I was still paying for hosting, babysitting plugin updates, and watching page load times get worse. Astro generates plain HTML files, so pages load instantly with no database or PHP involved. Hosting on Cloudflare Pages costs zero dollars. And I get to write in MDX files in a Git repo instead of fighting the block editor. That was enough for me.
If you are new to Astro, I have a guide on how to build a free blog with Astro and Cloudflare, and there is also my list of the best Astro.js online courses if you prefer video.
What the migration looked like
The short version:
- Exported everything from WordPress (posts, pages, images)
- Converted the XML export to Markdown files
- Used Codex CLI from OpenAI to automate the boring parts: image conversion, frontmatter formatting, fixing image references
- Built the Astro project and fixed what broke
- Redirected the old domain to the new one
I will walk through each step.
Step 1: Export your WordPress content
WordPress has a built-in export tool. Go to Tools → Export in your admin panel and export All content. You get an XML file with all your posts, pages, categories, tags, and image references.
A few things worth knowing:
- The export has image URLs but not the actual files. The converter in the next step downloads them for you.
- Custom post types or ACF fields might need extra handling. For a regular blog, the default export is enough.
- Back up your site first. You probably will not need the backup, but you will sleep better.
Before You Start
Make sure your WordPress site is still accessible during the migration. The markdown converter needs to download images from your live site.
Step 2: Convert WordPress XML to Markdown
wordpress-export-to-markdown is a Node.js tool that reads your XML export and spits out Markdown files with frontmatter. It also downloads all the images.
Install and run
npx wordpress-export-to-markdown
The tool walks you through an interactive setup: where the XML file is, where to put the output, how to handle images. I saved images, skipped year/month folders (wanted a flat structure to reorganize later), and excluded drafts.
When it finishes, you have a folder of .md files and a directory with all the images.
What the output looks like
Each post becomes a Markdown file with basic frontmatter:
---
title: "Your Post Title"
date: "2023-05-15"
---
Your post content here with images referenced like:

The frontmatter is bare-bones. You still need to add fields your Astro content collection expects (description, categories, tags, canonical URL, and so on). That is where Codex CLI came in.
Step 3: Let AI handle the grunt work
This step saved me hours. I used Codex CLI from OpenAI to handle the repetitive file-by-file changes that I was not going to do by hand for 50+ articles.
Convert images to WebP
The WordPress export had a mix of PNG and JPEG files, many of them oversized. I had Codex CLI batch-convert everything to WebP:
# Codex CLI handled the batch conversion
# Converting all PNG/JPEG images to WebP format
for img in images/*.{png,jpg,jpeg}; do
cwebp -q 80 "$img" -o "${img%.*}.webp"
done
WebP files run about 25-35% smaller than equivalent JPEGs at similar quality. Across a whole blog, that is a lot of bandwidth saved.
Update image references
After converting images, all the Markdown files still pointed to .png and .jpg filenames. Codex CLI went through every file and swapped the extensions:
<!-- Before -->

<!-- After -->

Format frontmatter for Astro
My Astro site uses a Zod-validated frontmatter schema. The exported files had almost nothing in the frontmatter, so Codex CLI reformatted each one to match:
---
date: 2023-05-15T00:00:00Z
title: "Your Post Title"
description: "A brief description pulled from the first paragraph"
image: "../../assets/images/your-post/featured.webp"
categories: ["cms"]
authors: ["Dragos"]
tags: ["wordpress", "tutorials"]
canonical: "https://www.bitdoze.com/your-post-slug/"
---
Move images to the right place
Astro optimizes images best when they live in src/assets/ rather than public/. Codex CLI moved each post’s images into the right directory structure:
src/assets/images/
└── post-slug/
├── featured.webp
└── screenshot-1.webp
Codex CLI was the real time-saver
Without it, I would have spent a full day just on image conversion, reference updates, and frontmatter formatting. Codex handled all of it in minutes. I just reviewed the output and fixed the occasional mistake.
Step 4: Set up your Astro content collection
If you do not have an Astro site yet, go set one up first. My free Astro blog guide covers that.
Your content collection config (src/content/config.ts) defines what fields your posts need:
import { defineCollection, z } from "astro:content";
const posts = defineCollection({
schema: ({ image }) =>
z.object({
date: z.date(),
title: z.string(),
description: z.string().optional(),
image: image().optional(),
categories: z.array(z.string()).optional(),
authors: z.array(z.string()).optional(),
tags: z.array(z.string()).optional(),
canonical: z.string().optional(),
}),
});
export const collections = { posts };
Put your converted MDX files in src/content/posts/ and images in src/assets/images/.
Step 5: Build and fix what breaks
Run your Astro build to see what is broken:
bun run build
Things that tripped me up:
- Broken image paths where the reference in the MDX file did not match the actual file location
- Invalid frontmatter that Astro’s Zod validation rejected (it tells you exactly which field in which file, which is nice)
- HTML from WordPress that MDX did not like, mostly unclosed tags and unescaped special characters
- Missing component imports at the top of MDX files
I ran the build after each batch of changes, so when something broke I knew what I had just touched. Codex CLI also helped here by reading the build error output and fixing the problems automatically.
Step 6: Deploy and redirect
Once the build passes, deploy. I use Cloudflare Pages and wrote a guide on deploying Astro to Cloudflare if that is your setup too. You can also deploy Astro on a VPS if you prefer managing your own server.
Set up domain redirects
The last piece: redirect your old domain. I pointed wpdoze.com at bitdoze.com. How you do this depends on your DNS setup:
- In Cloudflare, use Page Rules or Bulk Redirects to 301 the old domain to the new one.
- With other providers, set up a 301 at the server level or use your registrar’s URL forwarding.
# Example Cloudflare Page Rule
wpdoze.com/* → https://www.bitdoze.com/$1 (301 redirect)
Don't Skip the Redirects
301 redirects tell search engines that your content has permanently moved. Without them, you lose whatever SEO value your old domain had built up, and visitors hitting old bookmarks or search results get a dead page.
Things I wish I knew earlier
A few things I picked up along the way:
- Run the build after every major change, not just at the end. It is much easier to find the problem when you know what you just changed.
- Use WebP for all images. The size savings are real and Astro handles them well.
- Do not try to manually edit hundreds of files. Use AI tooling like Codex CLI or scripts to handle bulk changes.
- Keep your old WordPress site running until you have verified everything works on the new site. Do not rush to take it down.
- Check your analytics after the migration. Make sure your pages are getting indexed and traffic is flowing through the redirects.
After the migration
Once your content is over, a few things are worth doing:
- I wrote a guide on Astro SSG build optimization because large sites can get slow to build. Worth reading if you have more than a handful of posts.
- If you are on Node.js, switching to Bun cut my build times down noticeably.
- You will probably want a contact form at some point.
- For analytics, I use Plausible proxied through Cloudflare Workers. I wrote up how to set that up.
Frequently asked questions
How long does the whole migration take?
Depends on how many posts you have. My site had around 50 articles and the whole thing took about a day. Most of that time went to reviewing what Codex CLI produced and fixing edge cases. The actual export and conversion was maybe an hour.
Do I need to know TypeScript to use Astro?
No. Plain JavaScript works fine in Astro components. TypeScript is optional, though I recommend it for content collection schemas because it catches frontmatter mistakes at build time. The Zod schema definitions are readable even if TypeScript is new to you.
What about WordPress comments?
Astro is a static site generator, so there is no built-in comment system. You can bolt on Giscus (uses GitHub Discussions), Disqus, or Hyvor Talk. Personally, I dropped comments entirely. Most of mine were spam.
Will I lose my SEO rankings?
With proper 301 redirects, rankings should carry over. I saw a small dip for about two weeks while Google reprocessed things, then it recovered. The faster page loads from Astro actually helped my rankings afterward.
Can I use this process for a WooCommerce site?
For the content parts (blog posts, pages), yes. But if you have a WooCommerce store with products, cart, and checkout, Astro alone will not replace that. You would need a headless commerce layer like Snipcart or Shopify’s Storefront API alongside Astro.
What if my posts use WordPress shortcodes?
Shortcodes do not convert. The markdown exporter dumps them as plain text. You will need to replace them with MDX components or just remove them. I used Codex CLI to find all shortcodes across my files and convert the common ones into MDX equivalents.
Wrapping up
Moving from WordPress to Astro took a day of focused work, and I am glad I did it. My site loads faster, hosting costs went from $X/month to zero, and writing in MDX files beats the WordPress editor for my workflow.
The tools that made it practical: wordpress-export-to-markdown for the initial conversion, and Codex CLI for all the file-by-file reformatting I was not going to do by hand. Astro’s content collections tied it all together with type-safe validation that caught mistakes at build time.
If you have been putting this off like I was, just start with a handful of posts and see how it goes. The process is more mechanical than creative, and the tooling handles most of it.