No, most websites do not render empty for AI crawlers — we measured it
"AI crawlers only see an empty skeleton" is the favourite line in GEO marketing. We tested it against 300 randomly drawn websites of real German small businesses — and the claim fails. Here are the numbers, including the minority where it genuinely applies.
of its visible text the typical local business website already ships in raw HTML — no JavaScript needed (median across 189 measurable sites). A non-rendering crawler sees nearly everything.
We refuted our own thesis — twice
The expectation: small businesses build on site builders and JavaScript themes, so a raw HTML crawler should come up empty on many of them. A first run across well-known brands found zero percent — which we blamed on the wrong sample: tech companies server-render well, of course.
So we repeated it with the right population: 300 randomly drawn websites of real businesses — pharmacy, café, trades, medical practice, hotel. Result: median coverage 98.8%. It wasn't the sample. The thesis is simply wrong.
The reason is mundane: most small businesses run WordPress, Typo3 or classic site builders — and those ship finished HTML. The JavaScript-only site is the exception, not the rule.
What is actually true — and for whom
There is a real problem, it is just far smaller than claimed: 4.2% of measurable sites (roughly 1 in 24) show a raw crawler less than 40% of their content. At the softer 60% threshold it is 12.7% — roughly 1 in 8. At n=189 the 95% confidence intervals are ±2.9 and ±4.7 points.
But where it hits, it hits hard. A Heidelberg bookshop ships 204 KB of HTML, 203 KB of it JavaScript — the entire visible text a crawler receives reads: "Buch-Markt … Error loading the app. Try again". The crawler does not get an empty skeleton. It gets an error message, and that is all it could ever quote.
A Rostock dental practice ships 2 KB containing exactly one line: the practice name. A jeans shop ships nothing but its navigation. For these businesses this is not a detail — the content of their website does not exist in the AI world.
The harder finding: the door is shut more often than the room is empty
More striking than the render gap was something else: 26 of 300 domains (8.7%) flatly refused the GPTBot user-agent (403/401). Plus one case that nearly fooled us: a server answered HTTP 200, but the body was its WAF's block page — with essentially no text.
That confusion is exactly why such numbers so often come out inflated: a shut door looks, in the data, like an empty room. We count them separately — a disguised block page is a block, not a rendering problem.
We nearly made the same mistake in reverse. Of the domains whose raw fetch failed, 13 answered a real browser perfectly well — partly because the server drops the connection specifically for the GPTBot user-agent, partly rate limits. Counting those as "dead" would be the identical error, mirrored. Genuinely unreachable (both raw fetch and browser fail) are 39 domains (13.0%).
The full funnel — what remains of 300
Every percentage above refers to the 189 measurable sites, not the 300 drawn. Otherwise "renders empty" blends into "domain no longer exists". Fully itemised: 189 measurable, 39 genuinely unreachable, 26 blocking GPTBot (403/401), 13 raw fetch failed but reached by the browser, 17 render timeouts (>60s), 13 too thin for a meaningful ratio (<50 rendered words), 2 pure redirect/frameset shells, 1 disguised block page.
That adds up to exactly 300 — the arithmetic closes, and every excluded domain has a named reason. If you want to recount: the raw data, one row per domain, is in the repo.
Sample & method — open and reproducible
Source: OpenStreetMap via the Overpass API (free, no key). Frame: 9,232 unique domains — every OSM business with a listed website (retail, trades, office, food, health, hotel) across 8 mid-size German cities: Bonn, Freiburg, Erfurt, Regensburg, Münster, Heidelberg, Rostock, Kassel. (Kiel and Osnabrück timed out on Overpass and are honestly excluded.)
Draw: 300 domains, Fisher-Yates with fixed seed 20260716. Random — so no cherry-pick — and reproducible, because the seed is right here. The list is fixed before anything is measured.
Measurement: per domain, the raw HTML fetched with the GPTBot user-agent (no JavaScript) against the rendered DOM (Playwright, 60s timeout). Coverage = visible raw words ÷ visible rendered words, text extraction identical on both sides. Both sides tolerate certificate errors equally — otherwise every site with an expired certificate drops out as "dead" while the browser displays it fine. Thresholds: <0.4 "near-empty", <0.6 "strongly incomplete".
Limits, openly: only businesses with a listed website (skews slightly "more established"). 8 cities, not a national quota. Homepages only. OSM branch entries carry the corporate website (lecreuset.de, vodafone.de are in the frame) — which biases downward, since large companies server-render well. Some of the rate-limit responses (429) may stem from our own concurrency. And coverage is not citability; a site with 60% server-rendered text is probably still usable to an AI. The numbers describe *this disclosed sample*, not "exactly X% of all German businesses".
FAQ
So rendering does not matter for AI visibility?
For most sites: it is not the bottleneck — that is what our own data says, even though it argues against the obvious sales pitch. For roughly 1 in 24 sites it is fatal, because the crawler gets almost nothing, or even an error message. The only way to know for YOUR site is to look — not to believe a blanket claim, in either direction.
Why does this differ from other "X% render empty" figures?
Two common causes, and we had both ourselves. First, population: measuring well-known brands or JS-framework showcases gives a different picture than measuring real businesses. Second, counting: if you count blocked requests (including block pages disguised as HTTP 200) and dead domains as "renders empty", the number climbs fast. We report each of those separately, and the total reconciles.
Where does the sample come from?
From OpenStreetMap via the Overpass API: every business with a listed website across 8 mid-size German cities (9,232 domains), then 300 drawn at random with the disclosed seed 20260716. Source open, tool and raw data in the repo, fully reproducible.
How do I check whether my own site is affected?
Quick self-test: `curl -s https://your-domain.com | wc -w` against what you see in the browser — if only a few dozen words come back while the page looks full, your content loads via JavaScript. For the visible effect, deeploupe's free checker shows whether ChatGPT & co. actually mention you. No signup.
As of July 2026