Back to Blog
Blog
Guard
Latest
April 2026

Why Your Page Lost 40% of Its Content Overnight

A pricing page was converting at 4.2% on Monday. Tuesday it was still returning 200 - no rollback, no uptime alert, no error in the logs. By Thursday conversions were down to 2.7%. Nothing crashed. The page just shed about 1,300 words of features, trust blocks, and FAQs. This is how modern apps actually fail.

The Real Failure

We see this every week. A page does not break. It just gets smaller. No deploy was rolled back, no PagerDuty fired, and the engineer on call never even got pinged. The first signal is always the same: someone in growth or sales notices the funnel sliding.

A real degradation we audited:

  • Status: 200 OK on every URL
  • HTML size: 110 KB -> 65 KB (-41%)
  • Visible text: 3,200 -> 1,900 chars
  • Word count: about 40% drop
  • Conversions: 4.2% -> 2.7% in 3 days
  • Uptime monitor: 100% green
  • Time to detection: about 3 days, after analytics caught up

The page still loaded. The page still rendered. The page just lost about 40% of itself, quietly, with every monitor reporting healthy. This is the same shape as the deploy-time DOM drops we covered in When Your Content Disappears After Deploy - but slower and harder to spot.

What's Actually Happening

Your app does not ship content. It ships a render pipeline. The server returns a shell. JavaScript hydrates, fetches data, evaluates feature flags, pulls in third-party scripts, and then assembles the actual page in the browser. When any link in that chain partially fails, the page does not crash - it shrinks.

The browser still shows you a hydrated UI because your dev session is cached and your APIs are warm. The raw HTML - what bots, AI crawlers, and any cold-start render actually consume - looks like this:

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Acme - Pricing</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/assets/index-a3f7.js"></script>
</body>
</html>

Same shell shape we break down in Your Site Loads - But Google Sees Nothing and Script Shell Pages. The difference here is that the failure is not total - it is partial. The shell hydrates most of the page. It just drops a few sections.

Why Everything Looks Healthy

Every system you own says the page is fine:

  • 200 OK on every URL
  • Normal response times
  • No fatal exceptions in logs
  • Synthetic checks passing
  • RUM dashboards green
  • About 40% of the visible text is gone

Guard treats this exact pattern as a failure. The default thresholds:

Major text drop

More than 40% reduction in visible_text_length vs the previous scan.

Major DOM drop

More than 50% reduction in DOM nodes or html_bytes vs the previous scan.

This is not an uptime problem. It is a render integrity problem. See Your Site Returns 200 OK - But Is Completely Broken for the broader pattern of green-monitor-but-dead-output failures.

What We See in Production

Four patterns. None of them rare. All of them pass standard monitoring.

1

API regression returns an empty list

Cause: A backend deploy changes the response shape, or a query starts returning [] for a slice of users. Frontend treats empty as "nothing to render here."

Symptom: Pricing page goes from 6 feature blocks to 2. Word count drops from 3,400 to 2,000. No error is thrown - the empty-state branch just renders nothing.

Impact: Conversions drop the same day. Indexed pages start losing rankings within 1-2 weeks. See Why Your Site Randomly Breaks After Deploy.

2

Feature flag wipes whole sections

Cause: Marketing or product toggles a flag mid-deploy, or a flag defaults to false in production. Testimonials, FAQ, comparison tables - all gated behind that flag - disappear.

Symptom: HTML shrinks about 35%. The hero and nav still render so the page "looks fine" in QA.

Impact: Rankings degrade for keywords those sections supported. Conversions drop on pages that lost their CTA or trust block.

3

Hydration mismatch removes a DOM subtree

Cause: Server renders one tree, client builds a different one, React unmounts the divergent subtree post-load. Common after a dependency upgrade or any change that touches data fetched on the server.

Symptom: Half the page renders, the other half blanks. You only see it if you compare raw HTML vs the rendered DOM after hydration.

Impact: Slower decay than a bundle failure but harder to spot. See Hydration Crashes and React Blank Page in Production.

4

Third-party script fails silently

Cause: Stripe, your CMS SDK, or your analytics script 404s, times out, or gets blocked. The page still loads, but the components that depend on that script never mount.

Symptom: Pricing widget gone. CMS-driven testimonials gone. resource_error_count spikes if you measure it.

Impact: Partial page, often regional or browser-specific. Covered in detail in Critical JavaScript Failures.

Why Your Tools Miss This

Most monitoring checks the request, not the result. Did the server respond? How fast? Are there errors? It does not check how much content rendered, whether key sections exist, or whether the page is materially the same as yesterday. So a half-empty page passes every check.

RUM is no help either - it runs after hydration, on real users, with cached state. By the time RUM "notices," you have already shipped a degraded page to crawlers and cold visitors for hours or days. The sibling deploy-time pattern is the same: see When Your Content Disappears After Deploy.

Run These Tests Now

Do not take our word for it. Check your own pricing page or top blog post in under a minute and see what a bot actually receives.

Quick Test: What Do Bots Actually See?

~30 seconds

Most people guess. Don't.

Run this test and look at the actual response your site returns to bots.

1

Fetch your page as Googlebot

Use your terminal:

curl -A "Googlebot" https://yourdomain.com

Look for:

  • Real visible text (not just <div id="root">)
  • Meaningful content in the HTML
  • Page size (should not be tiny)
2

Compare bot vs browser

Now test what a real browser gets:

curl -A "Mozilla/5.0" https://yourdomain.com

If these responses are different, Google is indexing a different page than your users see.

Stop guessing — measure it.

Real example: 253 words vs 13,547

We see this constantly. Here's a real example from production: Googlebot saw 253 words and 2 KB of HTML. A browser saw 13,547 words and 77.5 KB. Same URL — completely different content.

Bot vs browser comparison showing 253 words for Googlebot vs 13,547 words for a rendered browser on the same URL

If your HTML doesn't contain the content, Google doesn't either.

Compare Googlebot vs browser on your site → HTTP Debug Tool
3

Check for common failure signals

We see this all the time in production:

  • HTML under ~1KB → usually empty shell
  • Visible text under ~200 characters → thin or missing content
  • Missing <title> or <h1> → weak or broken page
  • Large difference between bot vs browser HTML → rendering issue

Use the DataJelly Visibility Test (Recommended)

You can run this without touching curl. It shows you:

  • Raw HTML returned to bots (Googlebot, Bing, GPTBot, etc.)
  • Fully rendered browser version
  • Side-by-side differences in word count, HTML size, links, and content
Run Visibility Test — Free

What this test tells you (no guessing)

After running this, you'll know:

  • Whether your HTML is actually indexable
  • Whether bots are seeing partial content
  • Whether rendering is breaking in production

This is the difference between "I think SEO is set up" and "I know what Google is indexing."

If you don't understand why this happens, read: Why Google Can't See Your SPA

If this test fails

You have three real options:

SSR

Works if you can keep it stable in production

Prerendering

Breaks with dynamic content and scale

Edge Rendering

Reflects real production output without app changes

If you do nothing, you will not rank consistently. Learn how Edge Rendering works →

This issue doesn't show up in Lighthouse. It shows up in rankings.

Also useful: Sitemap Validator to confirm your indexable URLs are intact, and HTTP Status Checker to verify pages still resolve correctly across the site.

How to Detect It

Stop checking "does it load." Start checking "what loaded." Five concrete steps:

1. Measure HTML size

curl -s https://yoursite.com/pricing | wc -c

Track html_bytes per URL. Baseline 95KB and current 52KB is not noise - that is missing content. The browser will lie to you because it shows you the rendered DOM, not the raw response bots actually receive.

2. Measure visible text

curl -s https://yoursite.com/pricing | grep -oP '>[^<]+' | wc -w

Track visible_text_length and word_count. A baseline of 2,800 chars dropping to 1,600 is a broken page even if it looks "fine" to you. Set hard thresholds: under 50 words on a content page is broken. Period.

3. Compare snapshots over time

You need before vs after. Without baselines you cannot see gradual degradation - only catastrophic failure.

<!-- yesterday: 95 KB, about 2,800 chars rendered -->
<html>
<head><title>Pricing - Acme</title>...</head>
<body>
<h1>Pricing</h1>
<section class="plans">...full plans + 6 features...</section>
<section class="trust">...logos + testimonials...</section>
<section class="faq">...28 FAQ entries...</section>
</body>
</html>
<!-- today: 52 KB, about 1,600 chars rendered -->
<html>
<head><title>Pricing - Acme</title>...</head>
<body>
<h1>Pricing</h1>
<section class="plans">...2 plans, no features...</section>
<!-- trust + faq sections never rendered -->
</body>
</html>

4. Validate key elements explicitly

Check for the elements that define the page. Missing any of these is not an edge case - it is a failure:

  • Title tag present and correct
  • H1 present
  • Hero, pricing, FAQ, and trust blocks present in raw HTML
  • Primary CTA renders without JavaScript

5. Watch resource failures and console errors

Track resource_error_count per page and any console errors during render. A single failed third-party script can wipe a whole section without anything else looking off. If your monitoring does not capture per-page network failures, you are blind to this entire class of regression.

Practical Checklist

Run against the homepage and 5-10 critical URLs - pricing, top blog posts, signup - on every deploy and on a daily schedule. Fail loud on any hit. Silent skips are how you got here in the first place.

HTML size

track html_bytes per URL

  • Above your per-page baseline
  • No >30% drop vs the last scan
  • Not stuck at the shell (about 5-15 KB)

Visible text & word count

>= 1,000 chars / 300 words

  • No drop below 60% of baseline
  • Word count never under 50 on a content page
  • Title and H1 always present

Required sections

all critical blocks render

  • Hero / pricing / FAQ / trust / CTA
  • Sections render in raw HTML, not JS-only
  • Internal nav links present in raw HTML

API & flag hygiene

fail loud, never silent-skip

  • Treat empty arrays on required data as errors
  • Flags removing content default to safe values
  • resource_error_count and console errors stay at baseline

If you are not measuring content, you are not monitoring the page. By the time analytics tells you, you have already lost days of conversions and traffic.

Modern apps do not fail with errors. They fail by losing content.

A page can lose 40% of its text and still return 200 OK. If you are only watching uptime and logs, you will find out from traffic drops, conversion loss, and ranking declines - and by then it has already cost you.

How DataJelly Guard Catches It

DataJelly Guard monitors real page output - HTML size, visible text, DOM structure, missing sections, resource errors - across deploys and scheduled scans. Built for React, Vite, and Lovable apps where content depends on JavaScript. No app changes required.

  • Tracks html_bytes, visible_text_length, and word_count per URL
  • Flags major text drops (>40%) and DOM drops (>50%) immediately
  • Diffs raw bot HTML vs rendered DOM to expose script-shell pages
  • Alerts on missing critical sections (hero, pricing, FAQ, CTA)
  • Catches API empties, flag wipes, hydration mismatches, and silent script failures

FAQ