Twelve months migrating from WordPress to Astro on Cloudflare Pages
EN

Twelve months migrating from WordPress to Astro on Cloudflare Pages

Last verified: August 25, 2026
13 min read
Case study
500+ WP projects

The replatform from WordPress to Astro was supposed to be the project. It turned out to be the prologue. Exporting the content, rebuilding the templates, getting a static site to compile and deploy to Cloudflare Pages took weeks. Then the actual year began: redirects, hreflang, six-locale parity, and a build that outgrew the platform it was deployed to. This is a report on where the time went, because the time did not go where the planning said it would.

The polemic, if there is one, is with the framing of replatforming as a port. “Move off WordPress to a static site” sounds like a one-time migration. For a multilingual content site it is closer to taking ownership of three systems WordPress used to hide: the routing layer, the build, and the cross-locale structure. None of them are hard. All of them are continuous.

[!NOTE] Case at a glance

  • Project: wppoland.com replatformed from WordPress to Astro on Cloudflare Pages, a first-party rebuild of our own site
  • Scope: six locales, over 14,000 prerendered pages with descriptive slugs
  • Timeline: weeks to a working static build, about twelve months to stable, regression-free search performance
  • Build: exceeded Cloudflare Pages’ 8GB runner ceiling, resolved by building locally with a 16GB heap and deploying the artifact with Wrangler
  • Redirects: thousands of 301 rules that hit Cloudflare’s 100KB _redirects cap and moved into a Cloudflare Functions layer
  • Stack: Astro with Tailwind CSS, AVIF image pipeline, static HTML served from the edge
  • Outcome: global TTFB under 40ms, zero dynamic attack surface, predictable AI-crawler access, six-locale parity held

#WordPress to Astro migration, the real cost: TL;DR in 4 points

  1. The port is the cheap part. Templates and content export took weeks; the migration took about twelve months to reach stable, regression-free search performance.
  2. The redirect layer is the first surprise. Thousands of previously indexed URLs each need a 301, and the volume collided with a Cloudflare Pages file-size cap that silently dropped rules.
  3. Six-locale parity is continuous work, not a task. Hreflang, canonical URLs, and section structure have to stay aligned across every language version, forever.
  4. The build outgrew Cloudflare’s own runner. An 8GB build ceiling is not enough for 14,000 prerendered pages; the answer was to build locally with a 16GB heap and deploy the artifact via CLI.

#Glossary: static build, prerender, hreflang, edge

The report rests on a few platform terms:

  • Static build - the whole site is rendered to plain HTML files ahead of time, during a build step, rather than per request.
  • Prerender - generating each page’s complete DOM tree into physical HTML files. A six-locale site multiplies page count by the number of locales, so the build scales with content times languages.
  • Cloudflare Pages - the hosting platform that serves prebuilt files from the global edge network and runs serverless logic via Pages Functions.
  • Wrangler - Cloudflare’s command-line tool, used here to deploy a locally built dist/ directory directly, bypassing the platform’s build step.
  • Hreflang - HTML header attributes that tell search engines which URL is the local equivalent in another language.
  • 301 redirect - a permanent HTTP redirect that carries a moved URL’s ranking signal and index history to its new address.

#Weeks: the port that everyone budgets for

The visible migration is the part that gets estimated, and the estimate is roughly right. Content comes out of WordPress MySQL tables into Markdown files with YAML frontmatter. PHP templates and child themes are rewritten into Astro components styled with Tailwind CSS. The build compiles, and the static files deploy to Cloudflare Pages. A content site of moderate size reaches a working static build in weeks. This is the phase that demos well and convinces leadership the project is nearly finished.

In reality, the project is only at the beginning of its true engineering phase. A working build proves that Astro components can assemble HTML without syntax errors. It proves nothing about whether thousands of historical URLs still resolve properly, whether hreflang relationships remain intact across international Google indexes, or whether the Node.js build process can handle further corpus growth.

#Months: the redirect layer nobody scheduled

The first quarter of the long tail went to redirects. Every URL WordPress had ever exposed since 2006 (including date archives, categories, author tags, plugin taxonomies, and legacy slugs) needed a precise 301 redirect to its new Astro address. Without an exhaustive mapping layer, 404 errors surged in Google Search Console and historical search authority dissolved.

On a single-language site, managing redirects is a linear spreadsheet task. On a site serving six locales with descriptive translated slugs (such as Polish, German, Norwegian, Spanish, and Portuguese URLs), the list exceeded 18,000 distinct rules.

This volume exposed a hard platform ceiling: the _redirects file on Cloudflare Pages has an undocumented 100KB file-size limit. Past that threshold, the platform silently discards excess lines without throwing any build or deploy errors. As a result, earlier rules resolved correctly while later rules dropped into 404s. The fix was moving the entire redirect engine into an Edge middleware handler written in TypeScript (functions/redirect-map.ts), performing O(1) in-memory lookups at the request boundary.

#Months: six locales that have to agree forever

WordPress, with plugins like WPML or Polylang, conceals multilingual relationships behind database tables. A static SSG architecture exposes every relationship directly in files and markup.

Six language versions of every article must remain strictly parallel:

  • Identical technical H2 and H3 heading structure in matching logical order.
  • Complete bidirectional hreflang links in the document head pointing to all five language siblings.
  • Exact canonical URLs reflecting regional routing rules.
  • Consistent taxonomy tags across each language ecosystem.

When one locale drifts (such as adding an uncoordinated section or modifying a slug), search engines encountering asymmetrical hreflang tags start disregarding regional signals. This causes cross-market keyword cannibalization. To prevent this, we introduced automated cross-locale parity validation scripts run before every commit.

#Node.js memory topology at 14,000 prerendered pages

Astro compiles static pages within a single worker process. As the repository expanded past 14,000 pages (combining technical guides, architectural pillars, service landing pages, and local city hubs across 6 languages), the default V8 engine heap limit of 4GB was quickly exhausted.

Cloudflare Pages’ default build runner provides 8GB of RAM. During heavy TypeScript schema validation, MDX abstract syntax tree (AST) compilation, and Tailwind CSS tree-shaking, memory spikes triggered fatal JavaScript heap out of memory errors.

We resolved this by restructuring the entire deployment pipeline:

  1. Local Apple Silicon build execution: Builds run locally on M-series processors with explicit heap allocation via NODE_OPTIONS='--max-old-space-size=12288'. The machine compiles 14,477 HTML pages in under 3.5 minutes.
  2. Prerender artifact cleanup: During compilation, Astro outputs a dist/.prerender directory for server modules. Single bundle files reached 43.9 MiB, violating Cloudflare Pages’ 25 MiB single-file limit. The deploy script strips out these internal prerender chunks prior to upload.
  3. Direct Wrangler artifact deployment: The verified dist/ folder is uploaded directly to Cloudflare’s edge network, bypassing cloud runner memory limits completely.

#Modular sitemap generation and canonical graph routing

Another major hurdle was sitemap architecture. The standard @astrojs/sitemap integration generated a single monolithic XML file for 14,000 URLs, which broke crawling limits and failed to respect custom indexability rules.

We engineered a modular sitemap generator producing 32 interconnected XML files:

  • A root sitemap-index.xml pointing to six locale index files (sitemap-en.xml, sitemap-pl.xml, etc.).
  • Each regional index splits into specific leaf files: blog articles, service offerings, case studies, and city hubs.
  • A strict filter eliminating all noindex routes (guaranteed by the automated check:noindex-sitemaps script).

This modular architecture allows search engines and AI crawlers to ingest sitemaps incrementally without timing out.

#The tooling you rebuild that WordPress gave you free

A quieter cost of leaving a dynamic CMS is rebuilding the automated safeguards that WordPress and its plugin ecosystem handled behind the scenes. WordPress prevented duplicate slugs, maintained database integrity, and checked broken links during editing. In a static repository, any frontmatter typo or broken link ships directly to production unless intercepted by automated CI testing.

Over twelve months, we created a suite of 34 automated validation gates (run-gates.mjs) executed before every deployment:

  • Internal link integrity (check:links and check:service-navigation-parity): Scans all routes for broken links and ensures every core service receives at least two inbound internal links.
  • Diacritic and charset consistency (check:diacritic-wordlist): Catches character encoding regressions across Polish, German, Norwegian, Portuguese, and Spanish text.
  • Commercial pricing guard (check:no-own-prices): Audits informational content to prevent unapproved rate quotes from leaking outside dedicated pricing pages.
  • Content Security Policy validation (check:csp-inline): Scans HTML output to compute SHA-256 hashes for all inline scripts, enforcing strict CSP headers without unsafe-inline.
  • Editorial AI rhetoric audit (check:slop-rhetoric): Flags generic filler phrases, keeping content technically dense and actionable.

This testing harness delivers far greater reliability than WordPress admin panels, but required hundreds of dedicated engineering hours to construct.

#Media optimization pipelines and local typography

In dynamic WordPress setups, image resizing is handled on upload via GD or ImageMagick. In Astro, image optimization becomes a core build-time operation.

Passing thousands of high-resolution images through Astro’s image component initially caused severe build slowdowns and Sharp memory exhaustion. We introduced strict asset separation:

  1. Pre-compressed static assets: Large backgrounds and decorative graphics are pre-converted to AVIF and WebP in public/, serving directly from the CDN without compiler overhead.
  2. Deterministic dimensions for article graphics: In-content images require explicit width and height attributes, resulting in zero layout shift (Cumulative Layout Shift, CLS = 0.00).
  3. Self-hosted font subsets: We removed all external calls to Google Fonts. Font families are self-hosted in WOFF2 format with Latin character subsetting, eliminating render-blocking requests and ensuring full GDPR compliance.

#Astro Islands architecture for zero-JS interactivity

Astro’s core advantage is shipping zero client-side JavaScript by default. Readers browsing technical articles download purely static HTML and CSS.

Where client interactivity is mandatory (such as contact forms, filter selectors, or interactive calculators), we employ Astro Islands:

  • The contact form renders as a lightweight island using client:visible, deferring JavaScript execution until the user scrolls it into view.
  • Filter bars and category selectors utilize client:idle to load interactive scripts during idle browser frames, keeping the main thread responsive during initial page presentation.
  • Responsive viewport toggles take advantage of client:media="(max-width: 768px)", downloading mobile navigation scripts only on small screens and serving pure static HTML to desktop viewports.
  • Spam protection is handled via invisible Cloudflare Turnstile integration, avoiding user-facing CAPTCHAs.
  • Form submissions execute statelessly through an Edge API endpoint connecting to Resend webhooks, requiring no persistent backend server.

#Client-side WASM search without database overhead

WordPress traditionally relied on SQL queries against wp_posts for search functionality. In a static architecture, search must operate without a live database.

We deployed an in-browser static search engine powered by WebAssembly (WASM):

  • During the build, a compact, chunked lexical index is compiled from article bodies, omitting navigation menus, code blocks, and footer boilerplate.
  • The client downloads only small index chunks matching active search keystrokes (15-30 KB per chunk), rendering query results with instant fuzzy matching and term highlighting in under 15ms.
  • Multilingual stop-word dictionaries are compiled into each language sub-index, ensuring clean search recall without ballooning index size.
  • Server load remains zero regardless of concurrent search traffic.

#Continuous Core Web Vitals automation in CI/CD

In WordPress environments, Core Web Vitals often degrade silently after plugin updates. In Astro, performance budgets are enforced programmatically in the deployment pipeline:

  • Lighthouse CI: Every build undergoes automated checks verifying Largest Contentful Paint (LCP < 1.0s), Interaction to Next Paint (INP < 50ms), and Cumulative Layout Shift (CLS = 0.00).
  • Visual regression testing: Automated Playwright suites verify critical layout viewports on mobile and desktop, catching rendering glitches before merge.
  • Strict performance gates: If an updated component increases bundle size or blocks the main thread, the pipeline halts immediately with a clear diagnostic trace.
  • Instant edge cache purging: Upon artifact deployment, the release script invokes the Cloudflare Zone API (purge_cache: {"purge_everything": true}), ensuring global edge nodes instantly serve updated content without waiting for s-maxage expiration.
  • Deterministic immutable caching for hashed assets: Static CSS, JS bundles, and processed AVIF images are served with Cache-Control: public, max-age=31536000, immutable, while HTML documents carry Cache-Control: public, max-age=0, must-revalidate paired with edge caching tags. This allows instantaneous rollbacks and zero stale cache artifacts across global POPs.

#Technical migration checklist: 10 steps before decommissioning WordPress

Drawing on twelve months of live production data, here is the technical checklist required before switching DNS records:

  1. Complete route inventory: Extract every historical URL from WordPress MySQL tables (posts, pages, archives, categories, feeds, and attachments).
  2. Redirect architecture design: Construct an exhaustive 301 redirect map accounting for multilingual slug translations.
  3. Edge function deployment: Implement the redirect engine inside an Edge Function (Cloudflare Functions / Worker) to bypass static file caps.
  4. Hreflang mesh validation: Verify that all language versions include full reciprocal alternate links across the entire corpus.
  5. JSON-LD schema audit: Validate Article, FAQPage, HowTo, and Organization schemas against schema.org specifications.
  6. Modular sitemap deployment: Generate segmented sitemap files with strict exclusion of noindex pages.
  7. Heap and build profiling: Test build compilation under restricted memory and optimize V8 heap limits.
  8. Security header configuration: Enforce CSP, HSTS, X-Frame-Options, and Permissions-Policy at the edge CDN level.
  9. Indexability consistency audit: Ensure staging environments, technical pages, and duplicates are excluded from indexing.
  10. Post-cutover smoke testing: Configure automated HTTP smoke tests to monitor status codes and index health in Search Console immediately following DNS propagation.

#What the migration actually bought: 12-month engineering outcomes

After twelve months of production telemetry, the migration to Astro on Cloudflare Pages has proven thoroughly successful for our content and services platform:

  1. Server response time (TTFB): Dropped from 650-1200ms (under PHP/MySQL database load) to a consistent 25-45ms across Cloudflare’s global edge network.
  2. Attack surface elimination: Removing the PHP runtime, SQL database, and wp-admin portal eliminated 100% of standard CMS vulnerabilities (SQL injection, plugin RCE, brute-force authentication attacks).
  3. AI and search engine accessibility: Clean, semantic HTML without heavy JavaScript payloads allows search engines and LLM crawlers (OpenAI, Anthropic, Perplexity) to index technical content instantaneously, maximizing GEO/AEO discovery.
  4. Zero infrastructure scaling costs: Serving static assets from edge CDNs costs pennies compared to scaling dedicated MySQL database clusters under traffic spikes.

The honest engineering verdict: migrating from WordPress to Astro is not a superficial frontend rewrite. It is a comprehensive replatforming project where template porting is the easiest step, and the primary investment lies in routing architecture, automated validation gates, and multilingual governance. For organizations requiring global performance and zero maintenance overhead, it is an investment that pays dividends across every metric. If you want this architecture implemented for your team, discover how our Astro developer works, or explore our WordPress to Astro migration service. More architectural case studies are available on the WPPoland engineering blog.

Next step

Turn the article into an actual implementation

This block strengthens internal linking and gives readers the most relevant next move instead of leaving them at a dead end.

Want this implemented on your site?

If you are planning a Headless WordPress setup, frontend decoupling, or migration to Astro, I can design and build the architecture, API, and frontend.

Related cluster

Explore other WordPress services and knowledge base

Strengthen your business with professional technical support in key areas of the WordPress ecosystem.

How long does a WordPress to Astro migration actually take?#
The initial port (templates, content export, a working build) is a matter of weeks for a site of moderate size. The full migration, to the point where search performance is stable and nothing regressed, took about twelve months here. The long tail is not the port; it is the redirect map, hreflang across locales, parity between language versions, and scaling the build. Budget for the tail, not the port.
Why move off WordPress at all if it works?#
The trade is dynamic convenience for static performance and control. WordPress renders pages on request and gives you an admin and a plugin ecosystem; a static Astro build renders every page ahead of time and serves files from the edge, which is faster and has a smaller attack surface, at the cost of owning the routing and build yourself. It is worth it for a content site where speed, stability, and AI-crawler access matter more than in-dashboard editing convenience. It is not worth it for a site that lives on dynamic, logged-in functionality.
What was the hardest part of the migration?#
Not the templates. The redirect layer and locale parity. Every previously indexed WordPress URL needs a 301 to its new address, and on a multilingual site that list runs into thousands of rules, which collided with a Cloudflare Pages file-size cap. Keeping six language versions structurally identical (same sections, aligned hreflang, matching canonical URLs) is continuous work, not a one-time task.
Can Cloudflare Pages build a large Astro site?#
Serve it, easily. Build it, not past a certain size. Cloudflare's own Pages build runner has an 8GB memory ceiling, and a large multilingual Astro site with thousands of prerendered pages needs more heap than that to build. The fix was to build locally with a 16GB heap and deploy the finished artifact with Wrangler, rather than rely on the platform's build step.
Do images need special handling in Astro?#
Yes. Images imported through Astro's asset pipeline are optimized at build time, which is excellent for output quality but adds to build memory and time, and very large source images can push the build into an out-of-memory failure. The rule that held: pre- optimized, served-as-is background images go in the public directory; images that genuinely benefit from pipeline processing stay in the asset folder, kept reasonably sized.

Need an FAQ tailored to your industry and market? We can build one aligned with your business goals.

Let’s discuss

Related Articles

Cloudflare Workers and WordPress: serving WooCommerce at the edge

Cloudflare Workers runs JavaScript and WebAssembly at hundreds of data centres in 100+ countries worldwide. Pairing Workers with a WordPress origin moves the read path off the WordPress server and turns WooCommerce into an edge-rendered store. Here is how the architecture works, where it breaks, and what to measure before adoption.