Fixing a Low Text-to-HTML Ratio (and Why the Audit Flags It)

Every Deep Audit we have published in this series flagged the same finding at almost the same value: S2 2.7.02: Text to HTML ratio: 4%. The largest US injury firm, a regional dental group, a DTC Shopify store — different industries, different stacks, same number. Ninety-six percent of what they ship to the browser is markup and JavaScript.

Before the fix list, the honest framing, because this metric is widely oversold.

This is not a ranking factor, and anyone selling it as one is wrong

Google has never published a text-to-HTML threshold, and there is no evidence it scores one. If you are being sold "text-to-HTML ratio optimization" as a ranking lever, you are being sold a number, not an outcome.

We flag it anyway, because it is a cheap and reliable proxy. A 4% ratio is almost never a content problem on its own — it is the fingerprint of one of three underlying problems that genuinely do affect rankings and revenue. The ratio is how you notice; the causes below are what you fix.

Treat a low score as "open the hood," not as a defect to be optimized directly. You could raise the number by padding pages with text and change nothing that matters.

What it actually measures

Text-to-HTML ratio is the size of visible text content divided by the total size of the HTML document. A page that is mostly readable content scores high; a page that is mostly <div> scaffolding, inline styles, and embedded scripts scores low.

There is no magic threshold. As a working heuristic, under ~10% means something is bloating the document and is worth a look.

What the low ratio is usually telling you

1. The content is client-rendered. The initial HTML is a near-empty <div id="root"> and everything arrives via JavaScript. This is the one that actually costs you. Google renders JavaScript, but rendering is deferred and resource-constrained, and a page that renders as an empty shell when any link in the JS chain fails is fragile in a way a server-rendered page is not.

2. Third-party tag bloat. Chat widgets, ad pixels, A/B testing, analytics — each injects markup and script. In our ecommerce case study this showed up as 212KB of inline JS across 61 script blocks on a single product page.

3. Inline styles and framework output. Utility-class soup and per-element inline styles inflate the document with no content.

The reason we care is performance, not word count. Every one of those causes lands on Core Web Vitals — the markup and script has to be downloaded, parsed, and executed before anything useful paints. A low ratio and a poor LCP travel together consistently enough that the ratio is a useful early warning.

How to fix the causes

Server-render the primary content. The single biggest lever. Your main heading, body copy, and key links belong in the initial HTML response, not injected after hydration. Next.js, Astro, and similar frameworks make this the default — use it.

Defer non-critical JavaScript. async/defer everything that is not render-critical. Most chat and ad scripts have no business blocking first paint.

Trim unused CSS. Run a coverage pass in Chrome DevTools. Large sites routinely ship 300KB+ of CSS where 30KB is used.

Load the hero image eagerly, everything else lazily. The LCP element should never be loading="lazy" — see optimizing LCP and fetchpriority. Lazy-loading below the fold keeps the initial document lean; lazy-loading the hero defers the exact thing the visitor is waiting for.

What this metric will not tell you

It is a document-size measurement, so it is blind to several things worth knowing:

  • It does not distinguish useful markup from bloat. A well-structured page with rich schema and accessible landmarks pays a ratio penalty for doing the right thing.
  • It does not measure content quality — padding works.
  • It says nothing about whether the text is above the fold, or rendered at all.

Read it alongside the performance section and the content-semantics section. On its own it is a hint, not a verdict.

Confirm the fix with a re-audit

Every audit renders in real headless Chromium, so the score reflects what a browser actually sees rather than a static-HTML approximation. Fix, redeploy, re-run:

curl "https://seoscoreapi.com/audit?url=https://example.com" \
  -H "X-API-Key: YOUR_KEY"

Watch the ratio climb — but judge the work by whether the Performance category moved with it. If the ratio improved and LCP did not, you padded the page instead of fixing it.


See plans and pricing → — Pro and Ultra include unlimited Deep Audits if you want the full nine-section breakdown behind this finding.