Case study

The Wax Removal Co.

A microsuction ear wax removal clinic, built so the site holds no patient data at all. Second site on a shared in-house WordPress framework, consumed by Composer and version-pinned.

2026Solo build, custom WordPress theme on an in-house frameworklivethewaxremovalco.com
  • WordPress
  • PHP 8.4
  • Composer
  • Custom theme
  • wp-base
  • GDPR
  • LiteSpeed
  • GitHub Actions
thewaxremovalco.com homepage screenshot
thewaxremovalco.com homepage screenshot

What it is

A single-clinician microsuction ear wax removal clinic in Blaydon. Brochure pages, a gallery, reviews, and a booking link. Live since July 2026.

The interesting part is what it deliberately does not have.

The decision that shaped the build

No forms. No accounts. No shop. Booking goes to Fresha, which means every appointment record and every piece of health information lives in Fresha's systems rather than in WordPress.

That was a choice, not a shortcut. A clinic booking is special-category data under GDPR Article 9, and the cheapest way to handle special-category data correctly is to never hold it. A contact form posting into wp_postmeta would have put clinical enquiries inside a WordPress database, inside backups, inside whatever plugin later reads that table. Removing the form removes the entire class of problem, along with the breach surface, the retention policy, and the subject access request that would otherwise have to be answered out of a CMS.

The trade is real and worth naming: analytics on the booking funnel now stop at the outbound click. For a one-clinician practice that was the right side of the trade.

Built on a shared framework, not a copied theme

This is the second site on wp-base, an in-house WordPress framework I maintain as a Composer package. The theme declares which modules it wants in functions.php and bridges only the values that have to be computed late.

  • Consent
  • Seo
  • Security
  • Comments
  • Assets
  • Blocks

The version is pinned in composer.lock and checked automatically: a custom lint reads the lockfile against the framework's latest stable tag and fails the build on a major version lag. Both live sites currently sit on the same release. Behind that shared behaviour are 146 PHP tests carrying 270 assertions, plus 88 JavaScript tests.

The rule that keeps it honest is that a bug gets fixed upstream and the pin gets bumped, never patched around in the site. Letting that slip is what produced the divergence the framework was built to stop, and that story belongs to wp-base.

Deployment, and what a green deploy does not prove

Deploys run from GitHub Actions on manual dispatch only. No push to any branch can reach the server, which makes "deploy" a decision rather than a side effect of merging.

Two things this site taught me, both by going wrong first:

  • A page cache returns 200 whether or not your deploy landed. This site sits behind a LiteSpeed page cache and Cloudflare. A post-deploy health check that asserts an HTTP status will pass against a stale cached copy of the previous build. The check now asserts content, specifically a string that changes every release, so a pass means the new build is actually being served.
  • Purging the cache does not purge the homepage. After a full purge, interior pages served the new build while / kept serving the old one on a cache hit. The homepage needs purging by URL as well. That is now a two step runbook rather than a thing I remember.

Consent is default deny. Nothing loads until the visitor chooses, and the choice is a real reject rather than a dismiss button that counts as acceptance.

What I would change

Third-party embeds are the hardest part of consent on a brochure site, and the reason is structural rather than technical: a map or a video is content the client asked for, and the compliant version of it is worse to use. Click to load is the pattern that resolves that honestly, and it is what I would build in from the start rather than retrofit.

The other thing I would revisit is measurement. Sending every booking to Fresha was the right call and it has a cost: the funnel ends at an outbound click, so there is no way to tell a visitor who booked from one who bounced on the next page. For this clinic that trade is worth it. For a practice with several locations and a real ad budget it would not be, and the answer there is server-side event tracking rather than putting patient data back into WordPress.

This site stores nothing until you opt in. Pick a setting below. You can change it any time from the footer. Privacy policy.