Case study
Client website re-platform, Webflow to Astro on Cloudflare Pages
A portfolio client's 128-page marketing website rebuilt after diagnosing a 37% organic-click decline against improving rankings. Built on an unindexed preview without touching DNS, forms, or tag management, verified against production with a visual-diff harness, and delivered with a cutover runbook.
Built and verified ahead of cutover
- Role
- Sole engineer, from diagnosis through runbook
- Scope
- 128 pages, 17 CMS collections, 735 assets
- Stack
- Astro static build, Cloudflare Pages, GitHub with PR preview deploys and protected main
- Pipeline
- 22-script Python migration pipeline: extract, convert, verify
- Status
- Built, QA complete, merged. Cutover pending one external access grant. Not live.
Why
Organic clicks had fallen 37% while average ranking improved, and almost all remaining organic traffic was branded. That is a page-experience and click-through problem, not a content problem, and the platform was the cause: a render-blocking payload of shared CSS, the hosted runtime, and jQuery on every page. The client also wanted text edits to take minutes in a pull request instead of days in a designer-only tool. The re-platform was justified by the diagnosis, not by a preference for a newer stack.
Constraints I worked inside
- No DNS, form, or tag-management changes until cutover. The build lived on a preview blocked from indexing three ways.
- Never submit a live form. All 7 forms were verified by byte-matching their markup against the live DOM.
- Pre-existing live-site bugs (a broken form field, a broken redirect, an orphaned tag container) were reproduced faithfully, not silently fixed. Fixing them was scoped as a marketing decision, separate from the migration.
Engineering
- ExtractEvery page, collection, and asset pulled through the source platform's API. Images recovered at original quality rather than the lossy converted versions the platform served.
- Convert17 CMS collections mapped to Astro content collections with validated schemas.
- VerifyA visual-diff harness screenshots every page on both sites and compares them against a measured noise floor, so a real regression is distinguishable from rendering jitter. 384 comparisons. 12,629 internal links checked. Redirect coverage and metadata verified across all 128 pages.
- Cut weightjQuery, the hosted runtime, third-party plugins, and consent and tag scripts removed from the render path. Homepage transfer fell from 1,437 KB to 838 KB.
- Protect the repoProtected main with required review and no force-push, tested deliberately by trying to break it.
Delivery
- Cutover runbook with pre-reviewed DNS batches, certificate pre-validation to avoid an HSTS lockout, a go-live checklist, and a one-record rollback that takes five minutes.
- Plain-language editing guide so non-technical staff publish through GitHub pull requests and Claude Code.
- Engineering documentation covering the pipeline, the QA harness, and the deploy path.
Numbers
| Pages | 128 |
|---|---|
| CMS collections | 17 |
| Assets re-hosted | 735 |
| Migration pipeline | 22 scripts |
| Screenshot comparisons against production | 384 |
| Internal links verified | 12,629 |
| Forms byte-matched to the live DOM | 7 |
| Homepage transfer | 1,437 KB to 838 KB |
| Organic-click decline that prompted the work | 37% |
| Rollback time | five minutes, one DNS record |
This portfolio site is on the same stack: Astro static build on Cloudflare Pages, no client-side JavaScript.