Case Study — Ambulatory Endoscopy Center

Staff still edit WordPress.
Patients never load it.

A surgical center needed a public site its own staff could update — provider bios, procedures, patient instructions — without a developer in the loop, and without serving a database-backed CMS to patients on every request. WordPress stayed exactly where it was. Everything in front of it changed.

At A Glance ● live
59 pages built from WordPress
content, served as static files
Sector
Healthcare — surgical center
Stack
Astro 7 · WPGraphQL · Tailwind 4
CMS at request time
None — the site is static
Editing
WordPress, exactly as before
What Changed

Nothing changed for staff.
Everything changed for visitors.

Replacing a CMS is usually the expensive way to fix a slow site, and it costs the staff everything they know. Keeping the CMS and moving it out of the request path gets the same result without asking anyone to relearn their job.

× Every patient request went through WordPress — PHP, a database and a plugin surface in the path
Static HTML on a CDN; the CMS can be down and the site stays up
× A new page meant a developer, a deploy, and a wait
Staff create it in WordPress — page, provider, procedure or menu item — and the next build carries it
× 49 accessibility errors across the site, on 11 of 13 pages scanned
Zero confirmed WCAG 2.1 AA violations, verified on the live site by two independent scanners
× A slow or unreachable CMS took the public site down with it
The pages are already built — the CMS being down is a staff problem, not a patient one
× Images left over from the old site quietly 404'd, and shifted the layout as they loaded
Legacy URLs rewritten at build, with dimensions and lazy loading injected into CMS-authored images
× Meta descriptions written by hand, when they were written at all
Derived from the page's own content at build time, so editors get correct tags by doing nothing

Three rules the
build runs on.

01

The CMS is a build-time dependency

Staff keep the editorial surface they already know. WordPress exposes its content over GraphQL, the site fetches all of it while building — pages, providers, procedures, and the navigation menu itself — and ships plain HTML. Patients never touch WordPress. Every other decision here follows from that one.

02

A build that refuses to guess

Every response from the CMS is validated before it is used, so a renamed field or a restructured group stops the build loudly instead of quietly rendering a page that is wrong. Editor HTML is treated as untrusted on the way in, and where a page genuinely has nothing to show, it says so and tells search engines to leave it alone.

03

Accessibility as a program, not a claim

A healthcare practice's public site carries real accessibility obligations. This one was measured rather than asserted — two independent scanners, a test suite that drives a real browser for the things a scanner cannot see, and a rescan against the live site to confirm the fixes landed where visitors would meet them.

The CMS could be down all week. Patients would never know.

That is what fetching content at build time actually buys. The pages a patient loads were finished hours or days ago and sit on a CDN, so a plugin update, a slow database or an expiring certificate on the editorial side is a staff inconvenience rather than an outage on the public site. The same goes for the traffic spike nobody planned for: there is nothing to overwhelm.

Inside The Build

Making a build-time
dependency safe.

Moving the CMS out of the request path moves the risk into the build. A naive version of this architecture ships a broken site the first time WordPress is slow. Most of the engineering here is about that.

One way in, with a timeout

Every CMS query routes through a single client with a ten-second timeout and two retries on a backoff. Content fetching had started out duplicated inline across a dozen page files; consolidating it into typed services is what made timeouts, retries and validation a single change rather than twelve.

Validating what the CMS sends

WordPress schemas drift — fields get renamed, field groups get restructured, and IDs come back inconsistently typed. Every response is validated at runtime against a schema, so a shape change becomes a loud, catchable build failure instead of a page that quietly renders wrong.

Treating CMS HTML as untrusted

Editor HTML is injected into pages, which makes it a real attack surface if the CMS trust boundary ever moves. A sanitizing pass strips scripts, frames, forms, event handlers and javascript URLs — and while it is in there, it also rewrites broken legacy image URLs, injects missing dimensions, and removes the empty headings and anchors the block editor leaves behind.

A build that doesn't need the CMS to be up

Continuous integration runs a full build against a local mock GraphQL server on every push. Query and schema breakage is caught without depending on the live CMS being reachable from a build runner — and without a broken build ever reaching the site.

Failure states that stay out of the index

Not-found and fallback states carry noindex, so a bad build cannot leave a stub page in search results. Logging is environment-aware: debug and info are stripped from production builds while warnings and errors survive, without spilling payloads into logs.

A major version upgrade, zero source changes

Moving up a major framework version — along with the bundler, the compiler, the GraphQL client, Tailwind and the test runner — required no changes to the site's own code, cleared an outstanding dependency advisory, and produced a verified clean 59-page build. That is the payoff of the earlier consolidation, and the reason it was worth doing.

Accessibility

49 errors to zero
confirmed violations.

A healthcare practice's public site carries real obligations to the people who use it. This one was measured against WCAG 2.1 Level AA by two independent scanners, fixed, and rescanned on the live site rather than on a local copy.

Measure Before After
Errors found by static scans 49 0
Pages carrying an error 11 of 13 0 of 13
Interactive tests passing 10 of 19 19 of 19
Confirmed WCAG 2.1 AA violations 0
— Beyond what a scanner sees

A static scan reads markup. It cannot tell you whether a keyboard can reach the menu, whether focus is visible as it moves, whether the gallery traps focus once it opens, or whether the page still works for someone who has asked their device to reduce motion.

So those are tested directly, in a real browser, as part of the suite that lives in the repository — including the page reflowing properly at a 320-pixel viewport, which is where a phone in a waiting room actually sits.

— What the fixes were

Mostly small things that matter enormously to the person who hits them: a skip link that had been overlapping the header instead of clearing it, a headline sitting over a photograph where contrast could not be guaranteed, a map embed with no title for a screen reader to announce, and a page that rendered twelve pixels too wide on a small phone.

Motion now honours the visitor's own reduced-motion setting, and every image the CMS produces carries explicit dimensions so nothing shifts under a thumb mid-tap.

This is website redesign work for healthcare, and the reason it keeps working is ongoing maintenance.

Keep The CMS. Lose The Weight.

Your editors are not the problem.

Tell us what your team edits today and what your site has to do for the people who visit it. We will tell you whether the CMS needs replacing or just needs getting out of the way — and what either one costs.