Case study · This site

We audited our own site and it was the worst offender

It is easy to sell performance work and quietly run a slow site. Ours scored 30 on mobile, had a largest contentful paint of 11.1 seconds, and was completely invisible to any crawler that does not execute JavaScript. This is what we found and what we did.

Four independent problems

The page was rendered entirely in the browser. The served HTML contained an empty container and nothing else — so Bing, and the AI crawlers that increasingly decide what gets cited, saw a blank page.

Total blocking time was 69,920 milliseconds. A WebGL scene was running a continuous render loop on phones; under a throttled CPU every frame became a long task, and they kept accumulating while the page sat idle doing nothing.

The icon font was being requested across its entire variable axis range — 1,145 KB of transfer to draw thirteen icons. And Tailwind was being compiled in the browser on every single page load by a roughly 400 KB render-blocking script.

MetricBeforeAfter
Mobile Lighthouse performance3097
Largest contentful paint11.1 s2.5 s
Total blocking time69,920 ms~60 ms
Speed index21.3 s2.0 s
Font transfer1,145 KB52 KB
Image transfer1,736 KB75 KB
Third-party hosts50

The mistake worth reading

To make the page crawlable we prerender it into the HTML at build time, and then have React adopt that markup rather than rebuild it. The first implementation scraped the rendered DOM and wrote it back into the page. It looked completely correct in a browser.

It was not. React reported a text-content mismatch, escalated, and threw the entire tree away to re-render on the client — silently undoing the whole benefit while looking perfectly fine.

The cause is specific and worth knowing: React's hydration expects markup produced by its server renderer, which emits comment separators between adjacent text nodes. A scraped DOM loses them, because re-parsing merges the neighbouring text back together. The fix was to render the same component through React's own server renderer instead of scraping.

We only caught it because we did not trust the page looking right. We stamped a prerendered node before any script ran and watched the container for removals: five children torn out, five recoverable errors. After the fix, zero and zero.

What the audit found that was not about speed

The entire site source was publicly downloadable at a predictable path, alongside three work-in-progress copies of the homepage competing with it for the same keywords. A thin internal demo page was outranking the actual homepage for the company's own brand name. The Open Graph image was a 404, so every share rendered a blank card. There was no robots.txt, no sitemap, no favicon and no structured data at all.

And the site disabled text selection, blocked copy and cut, and overrode the print function to return false — so a visitor could not copy the phone number, and a prospect could not send the pricing page to their finance director. All to deter copying that anyone technical defeats in seconds.

Seven animations that had never run

Moving Tailwind to a build step surfaced a bug nobody had noticed. Seven keyframe animations were referenced by hand-written CSS but were never emitted, because Tailwind only outputs a keyframe block when it generates an animation utility that uses it — and none did.

The robot's arm had never moved. Its eyes had never blinked. The floating icons had never floated. They had been dead on the live site for as long as that CSS had existed.

Related

Want this built?

Tell us what you are trying to ship and we will tell you honestly whether we are the right team for it.