Blog post management dashboard with 22 total posts, 21 published, and a list of post titles, statuses, and dates.
wordpress cloudflare astro modernization serverless

What I Replaced WordPress With

Devon Hillard · · 6 min read

For years, justblackmagic.com ran on WordPress. It worked fine. But it kept eating weekend hours, and a couple months ago I got tired of it and rebuilt the whole site on Astro and Cloudflare Workers. Here's what actually changed.

The WordPress tax

None of this is unique to me. WordPress earned its ubiquity, and most of these complaints are the price of being that flexible. They still add up.

Plugin sprawl. Every feature you want is a plugin. Every plugin is a third party with its own update schedule, its own CVEs, and its own opinions about how your site should behave. Half the plugins I'd accumulated over the years were doing things I no longer needed. The other half were doing things I could now write in forty lines of TypeScript.

The update treadmill. Core, themes, plugins, PHP. Something needed updating every week. Skip it and you accept the security risk. Apply it and you might break something. Either way it's eating weekend time on a marketing site.

The attack surface. A WordPress admin login lives at a known URL on every WP site online. Bot traffic against /wp-login.php is constant and creative. You can harden it. You can't make it disappear.

Hosting cost. Decent managed WP hosting starts around thirty dollars a month and scales with traffic. For a consulting site that does a few thousand pageviews a month, most of that bill is paying for an always-on database I barely use.

Performance. Even with caching plugins doing their best, you're rendering PHP against MySQL on a single origin and hoping the CDN catches it. The first hit from a new region was always slow enough that I noticed when I traveled.

Add it up. WordPress was eating a few hours a month, and worse, I was starting to resent my own site.

What replaced it

The new stack:

  • Astro for the framework. Pages render to static HTML at build time. The bits that actually need a server (admin panel, form endpoints, AI authoring helpers) opt into SSR.

  • Cloudflare Workers for hosting. The whole site is one Worker, deployed globally. There is no origin server.

  • D1 (SQLite at the edge) for leads, subscribers, and redirects. R2 for media and the raw post bodies. KV for the read-side cache that public pages actually hit.

  • Cloudflare Access for admin auth, so there's no login form on the public internet. Turnstile in front of contact forms instead of an Akismet subscription. Email Workers for transactional mail, locked to one address so a misconfiguration can't fan out.

  • A custom React admin with a TipTap-based editor, AI assist on a bubble menu, and a publish flow that writes through R2 and rebuilds the KV indexes the public site reads.

I'll spare you the architecture diagram. The shape that matters is static-by-default with edge functions where I actually need a server. No always-on database. Nothing to SSH into.

What got better

Performance. The site is fast everywhere because it lives everywhere. No cold start, no first-hit penalty, no roundtrip from PHP to MySQL. Time to first byte is dominated by network latency to whichever Cloudflare PoP you hit, and that's usually close.

Cost. Comfortably under what I was paying for managed WP, enough that I stopped caring about the line item. Cloudflare's free tiers handle most of what a site this size needs, and what isn't free is metered to actual use.

Security posture. No PHP. No plugin CVEs. The admin lives behind Cloudflare Access: SSO with a policy, JWT-validated at the edge, no password to leak. The login URL bots have been hammering for years just doesn't exist anymore.

Ergonomics. The editor is finally something I want to use. I designed it for myself. The AI assist runs on a model I picked, with no plugin trying to upsell me to a paid tier. When something annoys me I open the codebase and fix it.

Operational sanity. Schema lives in versioned migrations that CI applies before each deploy. Bindings are declared in one config file. Secrets live in the platform itself rather than a file checked into git. The whole system rebuilds from a clone and a couple of API tokens.

It's legible to AI. This wasn't even a design goal. It's been the biggest surprise of the rebuild. The codebase is small and consistent enough that Claude Code can read it end to end. The data lives in shapes I designed myself, with no sprawling WP schema to navigate. Admin operations are typed functions an agent can call directly. In practice I can ask Claude to tail the 404 log, query D1 for a stuck record, draft a post, fix a redirect, or build a small admin feature, and it just works without me opening the editor. The site is genuinely agent-operable. The old stack couldn't touch that.

What got worse, or at least different

I'm not going to pretend any of this is free.

It got built. Russell did the heavy lifting on the initial framework: the Astro scaffold, the editor bones, the early shape of the admin. I've layered features on top since. WordPress hands you a CMS in an afternoon. This took weeks of focused engineering between the two of us. If your alternative is paying thirty bucks a month and forgetting about it, the math may favor staying.

No plugin ecosystem. Want comments, a membership wall, a forum, an LMS? In WordPress you install a plugin. Here you build it or wire in a third-party service. The flip side is that the codebase is small and consistent enough that Claude Code can land most custom features in a single session, with clean types, clear conventions, and no plugin compatibility gauntlet. There's still a tax for going custom. It's gotten a lot smaller over the last year.

Operational complexity moved. Where I used to click around wp-admin, I'm now reading D1 migration logs and KV cache state. The complexity lives in code I own and can reason about, which I prefer. It's still complexity I have to manage.

Editor parity is real engineering. Making the admin's WYSIWYG render exactly like the published page took deliberate work, including shared editor extensions on both sides. WordPress gives you that for free.

Net of all that: the rebuild is a capital investment that pays back in maintenance time you stop spending. If you plan to keep the site for years, the math works. If you're churning sites every two years, it probably doesn't.

The pattern beyond WordPress

WordPress is one example of a broader pattern. Incumbent monoliths earn their position over years. They accumulate a thick layer of plugins, modules, and customizations to stay relevant. Eventually they charge a steady tax in upkeep, attack surface, performance ceilings, and developer experience. The same shape shows up in aging Drupal installs, Magento stores stitched together with extensions, on-prem CRMs nobody wants to touch, and ten-year-old Java monoliths whose original architects are long gone.

Modernization isn't always the right call. Plenty of these systems are still the right tool for the job, and ripping them out for fashion is its own mistake. But they share a failure mode. The cost of staying gets paid in small increments. A plugin update here. A perf complaint there. A security ticket next quarter. One day you realize the system is consuming more than it produces, and you've been so busy maintaining it that you missed the moment the math flipped.

If that sounds like a stack you own, there's leverage on the table. We do this kind of work for clients at Black Magic Consulting. The work starts with figuring out whether the incumbent is still serving you, and what a sane next step looks like if it isn't. No rebuilds for the sake of rebuilding.

This site is exhibit A. It's been a pleasure to work on ever since.