I pulled up Lighthouse on my phone for arcs.health and the numbers were bad enough that I refreshed twice, hoping for a fluke. They weren’t flukes. Then I checked covenant.clinic and got the same sinking feeling — not broken, just the kind of mediocre that quietly costs you patients who bounce before the page finishes loading. Both sites hit 100/100/100/100 on mobile Lighthouse within a few focused sessions, but the thing that got us there wasn’t what I reached for first.
The single biggest win was deleting code. arcs.health was shipping an entire SPA framework — a single-page app, the kind where JavaScript builds every page in the browser — to homepage visitors who just needed to read a headline and click a button. Every visitor paid the JavaScript tax for route code they’d never use. covenant.clinic was mid-migration off Webflow and still dragging runtime remnants behind every page load.
And both had robots.txt files that Lighthouse flagged as invalid, even though the source files in our repo were pristine.

The Starting Point
Two sites, different symptoms, same root cause: architectural overhead we’d stopped noticing.
| Site | Key Problems |
|---|---|
| arcs.health | Full SPA bundle on static homepage, oversized images, invalid robots.txt |
| covenant.clinic | Webflow runtime remnants, render-blocking CSS, CLS from hero images, stale CDN cache |
The temptation was to start tweaking — shave a few kilobytes here, defer a script there. That’s the wrong order, and I almost did it anyway.
Fix Architecture First, Optimize Second
The arcs.health homepage is a static marketing page. It doesn’t need a framework. But because the rest of the app used one, the homepage downloaded the entire bundle — router, state management, component tree — for every visitor who might never click past /.
The fix was a lightweight static HTML shell that only loads the SPA when someone navigates deeper. A few lines of inline JavaScript at the bottom of <body> check the path and inject the framework only when it’s actually needed:
the mechanism — lazy loading, critical CSS, and edge debugging give me the detail
Lazy SPA hydration — the homepage becomes a plain HTML+CSS shell; the framework bundle is injected only when the path differs from /. This works because browsers execute inline <script> synchronously during parse, so the branch runs before any network request is made for the bundle. No framework needed:
<script>
if (window.location.pathname !== '/') {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = '/assets/index.css';
document.head.appendChild(link);
const script = document.createElement('script');
script.type = 'module';
script.src = '/assets/index.js';
document.body.appendChild(script);
}
</script>Critical CSS inlining — to fix CLS after the non-blocking CSS experiment, above-the-fold rules are inlined in <style> at response time (a short build step using critical or a custom Bun/Node extractor). The full stylesheet still loads async; the inline block prevents the unstyled flash that causes layout shift.
Edge debugging — to catch what Cloudflare Bot Management was injecting into robots.txt, use curl -I plus a raw body fetch on the public URL, not localhost:
curl -s https://your-domain.com/robots.txt | head -20Compare that output against your source file. Any directive you didn’t write came from the edge. Cloudflare’s Managed Robots feature (under Bot Management settings) is the usual culprit; disabling it via the API restores origin control while leaving AI-bot rules intact.
Image variants — responsive <source> sets (WebP + AVIF with JPG/PNG fallbacks) were generated with sharp in a one-time build script. The key is matching sizes to your actual rendered breakpoints, not defaulting to 100vw.
For covenant.clinic, the architecture fix meant leaving Webflow’s hosting entirely. We stood up a dedicated Bun service — Bun is a JavaScript runtime, like Node but faster — took ownership of every response header, and built an explicit redirect map for legacy booking paths. No more mystery middleware between us and the browser.
The CSS Trap: When Faster FCP Hurts UX
Here’s the wrong turn.
On covenant.clinic, I tried a textbook optimization: converting the main stylesheet from a blocking <link> tag to a non-blocking preload pattern. The idea is straightforward — let the page paint before CSS arrives so the browser has something to show immediately.
FCP improved. FCP is First Contentful Paint, the moment anything first appears on screen. The metric went up, and I felt smart.
Then CLS spiked. CLS is Cumulative Layout Shift — how much the page jumps around while loading. On visually dense marketing pages, content paints before styles arrive. The user sees a flash of unstyled text and images, then everything snaps into place as CSS loads. Lighthouse FCP looks better. The actual experience is worse. Nobody wants to watch a page rearrange itself.
The fix was to inline the critical CSS — the styles needed for what’s visible above the fold — directly in a <style> tag at response time. This preserves render stability while eliminating the extra network round-trip for the stylesheet. CLS dropped to near-zero and we kept the FCP gains.
Lesson: A faster paint that shifts content around is worse than a slightly slower stable paint. Always check CLS traces after changing how CSS loads, not just the speed numbers.
The Robots.txt Mystery
Both sites had the same bizarre issue. I’d open the robots.txt in our repo, verify it was valid, then run Lighthouse and watch it fail. I did this loop three times before I thought to check what was actually being served.
The culprit was Cloudflare’s Bot Management feature — a setting that sits between our origin server and visitors, blocking bad bots. It was silently injecting a Content-Signal directive into the served robots.txt. That’s a non-standard directive Lighthouse doesn’t recognize, so it rejects the whole file. This happened at the edge — at Cloudflare’s servers — even though our origin files were correct.
The fix required using Cloudflare’s global-key API to disable managed robots injection while keeping AI bot blocking active. Once we handed robots control back to the origin, both sites passed clean.
Lesson: Always validate responses from the public URL. Your CDN — content delivery network, the edge servers that sit between your origin and visitors — is doing things to your responses you didn’t ask for. curl the production URL. Don’t trust localhost.
Image Optimization: Context-Aware, Not Blanket
For both sites, we generated responsive image variants sized to actual rendered dimensions — not a blanket “make everything smaller” pass:
| Asset | Before | After |
|---|---|---|
| arcs.health hero | 1 large JPG, all devices | 700w + 1000w WebP with JPG fallback |
| arcs.health logo | 1 large PNG | 300w + 600w WebP with PNG fallback |
| covenant.clinic hero | Unsized, single source | Explicit dimensions + 470w AVIF variant |
Every image got explicit width and height attributes. This is the cheapest win in the entire playbook — it takes seconds per image and eliminates layout shift warnings entirely by telling the browser how much space to reserve before the image loads.
Final Scores
| Metric | arcs.health | covenant.clinic |
|---|---|---|
| Performance | 100 | 100 |
| Accessibility | 100 | 100 |
| Best Practices | 100 | 96 |
| SEO | 100 | 100 |
| FCP | 0.8s | 0.8s |
| LCP | 0.9s | 1.8s |
| CLS | 0 | 0.011 |
covenant.clinic’s Best Practices deduction comes from third-party scripts (GTM, Clarity) — not something we control in first-party rendering.
What I Learned
Architecture before optimization. Deleting an unnecessary SPA bundle from the homepage did more than every image compression, script deferral, and cache tweak combined. Find the biggest structural problem and fix it first. Don’t reach for the fine-grain knobs until you’ve addressed the thing that shouldn’t be there at all.
Faster isn’t always better. The non-blocking CSS experiment improved one metric while degrading the actual experience. Metrics measure things that serve users — they aren’t the goal themselves. If you can’t look at the page and feel good about what you see, the number doesn’t matter.
Trust but verify at the edge. Your CDN is modifying responses you didn’t ask it to. Always check what users actually receive, not what you think you’re sending. One curl against the production URL would have saved me an hour of staring at a valid source file wondering why Lighthouse disagreed.
Cache purge is part of deployment. After every change that touches content paths, purge the affected URLs from the CDN cache. Otherwise you’re measuring yesterday’s site against today’s expectations, and the score won’t move no matter what you fix.
Accessibility is cheap. Adding aria-label attributes, <main> landmarks, and descriptive link text took minutes and moved Accessibility from the mid-90s to 100. No redesign required.
These aren’t principles I read somewhere. They’re the specific things that moved two real healthcare websites from “fine” — the word I used when I didn’t want to look too closely — to perfect scores. The playbook is reusable for any content-heavy site sitting behind a CDN.