WordPress → Astro
Move a WordPress content site to a repository-native Astro build while preserving content, media, authorship, URLs, redirects, metadata, and a recoverable production path.
Should you make this move?
Both platforms have a case. Compare what you gain with what you give up before scheduling the cutover.
WordPress
- Unmatched ecosystem breadth and content ownership support almost any publishing model
- Editors get a purpose-built publishing workflow instead of raw files
- Plugin, hosting, security, and performance maintenance accumulate over time
- Themes, extensions, and content models add maintenance and portability constraints
Astro
- Content-focused sites ship very little client JavaScript by default
- Managed deployments reduce infrastructure and release-management work
- Repository-based publishing and custom integrations require engineering ownership
- Runtime assumptions, platform services, and pricing models shape the architecture
Astro: Content-focused sites ship very little client JavaScript by default. This removes a major source-side concern: Plugin, hosting, security, and performance maintenance accumulate over time.
What you lose: Unmatched ecosystem breadth and content ownership support almost any publishing model. What you inherit: Repository-based publishing and custom integrations require engineering ownership.
Know the shape of the move.
This timeline assumes
- Profile: one WordPress site with at most 1,000 public URLs, 5 GB of media, 10 authors, and no ecommerce or logged-in customer area.
- You control WordPress Admin, the database and file backup mechanism, the source repository, hosting, DNS, analytics, and search-console accounts.
- Astro will own the content after cutover. Keeping WordPress as a headless CMS is a different architecture and is not covered here.
- Plugin-provided data is portable only when it appears in the export or API and has an explicit destination mapping.
- The destination is tested on a private preview hostname before production routing changes.
What survives the move.
“Partial” and “manual” are not footnotes. They are work that must be scheduled and verified.
| Item | Outcome | Impact | What happens | Mitigation |
|---|---|---|---|---|
| Posts, pages, dates, status, and body | partial | high | WXR includes core content, but WordPress body HTML, Gutenberg block comments, embeds, and generated markup can require cleanup before becoming Markdown, MDX, or structured Astro data. | Preserve the raw HTML field, transform a copy, and compare representative simple, block-heavy, embedded, and legacy posts. |
| Categories, tags, and custom taxonomies | clean | medium | WXR includes categories, tags, and custom taxonomies; Astro can store them as arrays or referenced content. | Normalize duplicate slugs and verify archive-page counts before cutover. |
| Authors and WordPress users | partial | medium | Authorship metadata can transfer, but WordPress passwords, roles, admin access, and user sessions do not become Astro authentication. | Create author records separately and map each post author by stable login or ID; rebuild authentication only if it is genuinely required. |
| Media library files | manual | critical | WXR references attachments but is not a binary backup. Original files, generated sizes, PDFs, captions, alt text, and remote embeds must be inventoried and copied separately. | Back up wp-content/uploads, download every referenced attachment, preserve metadata, rewrite URLs, and fail verification on any missing critical asset. |
| Featured images | manual | high | Featured images are linked through attachment IDs and metadata rather than a ready-to-use Astro image field. | Resolve each attachment ID to a copied asset and generate explicit image and alt-text fields. |
| Comments | manual | medium | WXR contains comments, but a static Astro site has no native comment store, moderation queue, identity, or notification workflow. | Archive comments, render them read-only, or import them into a chosen comment service; document the decision before transformation. |
| Custom fields and custom post types | partial | critical | WXR can contain custom fields and custom post types, but plugin schemas, serialized values, relations, and display logic are not automatically portable. | Inventory every public custom type and field, decode values explicitly, and block cutover when an unmapped field changes public behavior. |
| Shortcodes, widgets, and plugin-rendered blocks | manual | critical | The source tokens may export, but the PHP or plugin renderer does not run in Astro. | List every shortcode and dynamic block, replace it with an Astro component or static output, and scan the built site for unconverted tokens. |
| Menus and navigation | manual | high | Menu data can exist in an all-content WXR export, but theme locations, hierarchy, active-state behavior, and presentation need an Astro implementation. | Export the menu tree, map internal URLs, rebuild it as structured data, and compare every primary and footer navigation item. |
| Theme, templates, and visual settings | lost | high | The WordPress theme and Customizer behavior do not transfer to Astro; the destination is a new frontend. | Capture screenshots and design tokens only as reference, then define explicit destination templates and acceptance screenshots. |
| SEO plugin metadata and schema | partial | critical | Titles, descriptions, canonicals, robots directives, social cards, redirects, and structured data may live in plugin-specific custom fields or tables. | Export the active SEO plugin fields separately, map them into Astro head data, and crawl both sites to compare canonical, robots, metadata, and schema output. |
| Forms, search, accounts, and other runtime features | lost | critical | A static Astro build does not inherit WordPress form handlers, search queries, login, memberships, ecommerce, webhooks, or scheduled plugin jobs. | Choose and test an explicit replacement for every runtime feature; do not treat a visually correct page as functional parity. |
| Analytics history and operational logs | lost | medium | Historical analytics remains in its analytics provider; WordPress logs, revisions, drafts, and plugin audit history do not become Astro content history. | Export required reports and retain the read-only source backup for the agreed retention period. |
Where each thing goes.
| Source | Destination | Method | Notes |
|---|---|---|---|
| post_name | slug and output route | transform | Preserve the existing public path unless an explicit permanent redirect is recorded. |
| post_title | title | automatic | Decode entities and compare the rendered heading and document title. |
| post_content | body or content field | transform | Retain raw HTML before converting blocks, shortcodes, embeds, and absolute URLs. |
| post_excerpt | description or excerpt | transform | Distinguish authored excerpts from WordPress-generated excerpts. |
| post_date_gmt and post_modified_gmt | publishedAt and updatedAt | automatic | Use UTC source fields and render the intended display timezone. |
| post_status | draft and publication state | transform | Do not publish drafts, private posts, password-protected posts, or scheduled posts accidentally. |
| creator login or user ID | author reference | transform | Create the author collection first and map with a stable identifier. |
| category and tag terms | categories and tags | automatic | Normalize case, duplicate names, and term slugs. |
| _thumbnail_id | heroImage reference | transform | Resolve the attachment, copy the original, preserve alt text, and record missing assets. |
| attachment URL and metadata | local or destination media path | transform | Rewrite body, social, feed, and metadata URLs after copying files. |
| SEO plugin title, description, canonical, robots, and social fields | Astro page head and structured data | manual | The exact source keys depend on the installed plugin and version. |
| WordPress permalink | Astro route or permanent redirect | manual | Every source URL needs one deliberate destination and a single-hop result. |
Make the move recoverable.
Create the source-of-truth backup
Create a source snapshot that can restore the entire WordPress site, not just its posts.
- Record the WordPress version, active theme, active and network-active plugins, permalink pattern, site URL, home URL, PHP version, cron jobs, integrations, DNS records, and current production host.
- Generate an unfiltered Tools → Export WXR file and record its timestamp, byte size, and item counts.
- Back up the complete database and all site files, including wp-content/uploads, themes, plugins, mu-plugins, wp-config.php, and server configuration that affects routing.
- Export SEO plugin settings, redirect rules, form submissions and definitions, analytics reports, and any plugin data stored outside the core export.
- Create a source URL inventory from the XML sitemap, analytics landing pages, search-console data, and a crawl; combine and deduplicate it.
- Test the backup by opening the archives, validating checksums, and restoring to an isolated environment or using the host's documented restore verification.
Proof to capture: A dated manifest listing every archive, checksum, item count, URL count, restore owner, credential location, and the result of the restore check.
Extract content into a lossless staging model
Preserve source IDs and raw fields before choosing Markdown, MDX, or JSON output.
- Parse posts, pages, custom post types, terms, users, attachments, comments, and post meta without mutating the source export.
- Store the WordPress ID, type, status, slug, original URL, raw body, timestamps, author key, terms, excerpt, featured-image ID, and all custom fields.
- Write an exception report for malformed XML, duplicate slugs, missing authors, unknown types, and unserialized values.
Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.
Convert body content
Produce destination content without silently dropping blocks or dynamic tokens.
- Choose raw HTML for maximum fidelity or Markdown/MDX for repository editing; do not mix formats without a rule.
- Replace every known shortcode, plugin block, embed, gallery, caption, and internal link using a documented mapping.
- Scan output for WordPress block comments, shortcode brackets, source-domain URLs, empty images, iframes, and unsupported markup.
- Render representative posts from each content era and compare their headings, links, images, tables, code, embeds, and calls to action.
Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.
Copy and rewrite media
Remove runtime dependence on the WordPress uploads host.
- Resolve all attachment IDs and every media URL found in body, metadata, menus, CSS, feeds, and social fields.
- Copy original files, preserve stable filenames where possible, and record source URL, destination path, byte size, content type, and checksum.
- Rewrite references only after the destination asset exists; list missing, duplicate, oversized, or externally hosted assets separately.
Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.
Generate routes, redirects, and metadata
Preserve inbound links and search signals while adopting Astro routing.
- Assign every source URL to the same route, a deliberate new route, an external destination, or an approved removal.
- Generate permanent redirects for moved URLs and remove loops, chains, wildcard collisions, and redirects that shadow real routes.
- Generate title, description, canonical, robots, Open Graph, social image, feed, sitemap, and structured data from mapped fields.
Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.
The things most likely to hurt.
These are operating limits. Treat every “Stop if” condition as a blocked migration, not a suggestion.
Mistaking WXR for a full backup
Tools → Export creates a content XML file. It does not replace database, uploads, theme, plugin, configuration, and DNS backups.
- Consequence
- Missing binary assets or plugin data can make rollback or reconstruction impossible.
- Mitigation
- Require separate database and file archives plus a restore check before transformation.
Stop if: The source cannot be restored independently of the live host.
Silently dropping plugin-rendered behavior
Shortcodes and plugin blocks can survive as text while their renderer disappears.
- Consequence
- Pages may build successfully while forms, embeds, tables, calls to action, or protected content are broken.
- Mitigation
- Inventory token types, define a replacement for each, and scan the final build for unconverted patterns.
Stop if: Any production-critical shortcode, block, form, or runtime feature lacks a tested replacement.
Publishing private or scheduled content
A naïve exporter can treat every WXR item as public content.
- Consequence
- Draft, private, password-protected, trashed, or future content can leak.
- Mitigation
- Map every source status explicitly and generate a pre-deploy list of destination public routes.
Stop if: Destination public-route counts cannot be reconciled with source published-content counts.
Changing DNS before URL and feature parity
Do not route the domain to Astro because the homepage looks correct.
- Consequence
- Deep URLs, media, forms, metadata, feeds, redirects, or analytics can fail for users and crawlers.
- Mitigation
- Run blocking checks against the preview using the complete critical URL set first.
Stop if: Any high-value URL lacks a 200 response or deliberate single-hop permanent redirect, or any critical runtime check fails.
Losing the final editorial delta
Content created after the first export will not exist in the destination unless it is frozen or imported again.
- Consequence
- Recent posts, edits, comments, form entries, or media disappear at launch.
- Mitigation
- Announce a freeze, record its exact start time, run a final delta capture, and reconcile timestamps.
Stop if: The source changed after the final capture and the delta has not been imported and reverified.
Do the work in this order.
- Day 1 · inventory-backup
Inventory and prove the backup
3–5 hours active1 day elapsed1–4 hours for large archives or host restore jobs waiting- Freeze architecture changes, record platform configuration, and inventory content types, plugins, features, integrations, and URLs.
- Create WXR, database, file, media, SEO, redirect, analytics, and plugin-specific exports.
- Validate checksums and prove the restore path.
Stop / go checkpointIs the migration recoverable?
Go when: Every public feature has an owner and the complete source can be restored without the production server.
Stop when: Any required data, asset, credential, or restore mechanism is missing.
- Day 2 · extract-transform
Extract, transform, and report exceptions
4–7 hours active1 day elapsedUp to 2 hours for media copying waiting- Create the lossless staging model and reconcile source counts.
- Convert body content, fields, taxonomies, authors, status, shortcodes, and media.
- Generate exception reports for every unsupported or ambiguous record.
Depends on: inventory-backup
Stop / go checkpointIs every exception assigned?
Go when: Counts reconcile and every exception is fixed, accepted as a named loss, or assigned for manual work.
Stop when: The transformation silently drops records or public behavior.
- Day 3 · preview-build
Build the private destination
3–5 hours active1 day elapsed30–90 minutes for clean build and preview deployment cycles waiting- Build Astro routes, templates, navigation, metadata, sitemap, feeds, runtime replacements, and redirects.
- Deploy to a private preview hostname with production-like environment values.
- Crawl the preview and compare representative visual and functional samples.
Depends on: extract-transform
Stop / go checkpointIs the preview production-complete?
Go when: The production build is clean and all blocking content, URL, asset, metadata, and runtime checks pass on preview.
Stop when: The build hides warnings, exposes private content, or depends on the live WordPress runtime unexpectedly.
- Day 4 · rehearsal
Rehearse cutover and rollback
1–2 hours activeHalf day elapsed30 minutes between routing checks waiting- Run the exact cutover checklist without changing public routing.
- Verify access, deploy permissions, DNS ownership, monitoring, rollback steps, and communication owners.
- Time the final export, build, deploy, verification, and rollback actions.
Depends on: preview-build
Stop / go checkpointCan the team cut over and roll back inside the window?
Go when: Named operators can complete both paths with working credentials and the measured duration fits the window.
Stop when: Any action depends on an unavailable person, unknown credential, or untested command.
- Day 5 · cutover
Freeze, capture the delta, and cut over
2–3 hours active2–4 hours elapsedAllow the DNS and CDN behavior observed during rehearsal waiting- Start the editorial freeze and capture the final content, media, comment, form, and redirect delta.
- Rebuild and deploy the immutable release; run blocking checks on the destination hostname.
- Change routing, run the production verification report, and announce only after blocking checks pass.
Depends on: rehearsal
Stop / go checkpointKeep Astro live or roll back?
Go when: Every blocking production check passes twice, analytics receives traffic, and no source delta is missing.
Stop when: A blocking check fails or the team cannot explain a source/destination count variance.
- Days 6–11 · monitor
Monitor before cleanup
15–30 minutes daily active7 days after cutover elapsedSeven-day rollback observation window waiting- Review errors, 404s, redirects, search-console coverage, analytics, forms, feeds, and user reports.
- Fix destination issues without altering the recoverable source.
- Approve cleanup only after the observation window and final report sign-off.
Depends on: cutover
Stop / go checkpointCan the source be retired?
Go when: Seven stable days pass with no rollback trigger and the signed report has no unexplained blocking exception.
Stop when: Critical errors, missing traffic, broken submissions, or high-value 404s remain.
Cut over with a way back.
Cutover
Move production traffic to one immutable Astro release after a final source delta and preview parity check.
- Begin the announced editorial freeze and record the exact UTC timestamp.
- Capture and import the final content, media, comment, form-entry, redirect, and configuration delta.
- Build from a clean checkout, save the commit and deployment IDs, and run all blocking checks on the destination hostname.
- Lower-risk option: change only the application origin or CNAME while preserving the DNS provider; record the before state.
- Wait for the observed routing change, then run availability, URL, redirect, asset, metadata, form, analytics, feed, and search checks from outside the operator network.
- Keep WordPress read-only and intact; announce completion only after two consecutive blocking-check passes.
Proof to capture: Freeze timestamp, final source/destination counts, commit and deploy IDs, before/after routing records, two passing verification runs, analytics event, form receipt, and named operator approval.
Rollback
Restore traffic to the unchanged WordPress source without losing changes made during the cutover window.
- Declare rollback, stop destination publishing, and preserve Astro deployment, build, access, and error logs.
- Restore the recorded origin or DNS values that routed to WordPress.
- Confirm WordPress database, media, login, forms, search, feeds, redirects, analytics, and critical URLs.
- Reconcile any destination-only edits or submissions into a protected holding file before reopening source publishing.
- Communicate the rollback, owner, reason, next decision time, and evidence location.
Proof to capture: The public domain serves the recorded WordPress release, critical URLs and runtime features pass, new submissions are captured, and editors can publish only on the restored source.
- Any critical URL, media, metadata, form, feed, search, account, or analytics check fails after one attempted correction.
- Published-content or media counts have an unexplained variance.
- Error rate, high-value 404s, or lost organic landing traffic exceeds the agreed threshold.
- The destination cannot be fixed and fully reverified within 60 minutes of the first blocking failure.
Prove the migration worked.
Every blocking check must pass. Capture the evidence before cleanup begins.
| Pass | ID | Check | Method | Expected result | Evidence |
|---|---|---|---|---|---|
CNT-01Blocking | Published item count | Compare published posts, pages, and public custom types by source ID and destination route. | Counts match exactly or every variance is an approved redirect, merge, or removal. | Attach the source/destination reconciliation CSV. | |
STS-01Blocking | Non-public content exclusion | Search the built route manifest for draft, future, private, password-protected, and trashed source IDs. | No non-public source item has a public destination route. | Attach the status-to-route audit. | |
URL-01Blocking | Critical URL inventory | Request all sitemap URLs plus top analytics and search-console landing pages against production. | Every URL returns its intended 200 page or one deliberate permanent redirect to the final 200 destination. | Attach status, final URL, redirect count, canonical, and response-time output. | |
AST-01Blocking | Media completeness | Request every copied or rewritten media reference and compare recorded size or checksum where available. | All required assets return the correct content type with no zero-byte, placeholder, or source-only dependency. | Attach the asset manifest and missing-asset report. | |
TOK-01Blocking | Unconverted token scan | Scan built HTML and content for known shortcode, Gutenberg-comment, source-host, broken-link, and placeholder patterns. | No unapproved token or source-runtime dependency remains. | Attach the zero-result scan or approved exception list. | |
SEO-01Blocking | Metadata parity | Compare title, description, canonical, robots, Open Graph, social image, structured data, sitemap, and feed for the critical sample. | Every field matches the approved mapping and canonical URLs use the production domain. | Attach the metadata comparison. | |
NAV-01Blocking | Navigation and internal links | Click every primary and footer item; crawl internal links and fragments. | All destinations are deliberate, reachable, and free of redirect chains or broken fragments. | Attach the crawl summary and manual navigation checklist. | |
RUN-01Blocking | Forms and dynamic replacements | Submit production-safe tests through every form and exercise search, embeds, protected areas, or other approved replacements. | Each action reaches its destination once, displays the correct result, and records required analytics. | Attach receipts, screenshots, log IDs, and analytics events. | |
ANA-01Blocking | Production measurement | Generate a labeled test pageview and conversion from a non-operator browser. | The intended production property receives one pageview and one conversion with the correct hostname and path. | Attach the realtime or debug-view capture. | |
RLB-01Blocking | Source remains recoverable | Check source host health, restore credentials, recorded routing values, and WordPress read-only access. | The source can be restored inside the measured rollback window without overwriting destination evidence. | Attach the rollback readiness checklist and last rehearsal duration. |
Remove the scaffolding safely.
Safe after: Seven stable days, a signed verification report, no unresolved high-value 404s, and explicit rollback-window closure.
- Create a final immutable WordPress database-and-files archive with checksums and retention owner.
- Export final analytics, search-console, redirect, form, and operational reports required for record keeping.
- Revoke migration-only credentials, API tokens, deploy keys, temporary admin users, and preview access.
- Disable source publishing before canceling anything; preserve a read-only restore path for the agreed retention period.
- Remove obsolete DNS records, build hooks, cron jobs, webhooks, SMTP routes, and plugin licenses only after confirming no destination dependency.
- Update the runbook, ownership, incident contacts, publishing workflow, backup schedule, and next review date.
- Cancel WordPress hosting last, after a second person confirms the archive and restoration instructions.
When the plan met reality.
First-hand accounts are preferred. Vendor case studies are labeled, and every note below is an editorial paraphrase—follow the link for the full context.
Tim Leland moved a WordPress blog with hundreds of pieces of content into local Markdown and rebuilt the theme in Astro with coding-agent assistance. The migration preserved URLs, slugs, canonical metadata, sitemap, RSS, structured data, and local assets. The resulting static site was cheaper and easier to recover, but the author cautions that traditional CMS workflows still suit multiple editors, approvals, commerce, and highly dynamic content.
- Preserve every public URL and SEO output, and move media locally so the new site is self-contained.
- Confirm that the site is genuinely static enough before replacing CMS workflows and enabling full-page edge caching.
- Local Markdown plus Git improved rollback, maintenance, and automated content review beyond the hosting change itself.
- Astro was a strong fit for this mostly static blog, not a universal replacement for collaborative or dynamic WordPress sites.
After 15 years on WordPress, Michaël Hompus mirrored the existing site with wget, exported posts through the Jekyll Exporter plugin, and used an open-source wordpress-to-astro project as the starting point. The export was imperfect but sufficient, comments were intentionally excluded because they had already been disabled, and the author rebuilt layouts while retaining the site’s visual character.
- Create an independent static mirror before transformation so pages and assets remain available for comparison.
- Treat exporter output as raw material and budget time to rebuild layout, semantics, accessibility, and unsupported content.
- Combining a site mirror with a post export provided better recovery coverage than relying on one export method.
- Leaving already-unused comments behind simplified the project without sacrificing an active reader workflow.
Built to be reviewed.
Tested 2026-07-18. Next scheduled review: 2026-10-18.
- WordPress Tools Export screenAccessed 2026-07-18
- WordPress backupsAccessed 2026-07-18
- WordPress REST API referenceAccessed 2026-07-18
- Astro content collectionsAccessed 2026-07-18
- Astro routing and redirectsAccessed 2026-07-18