SPA-SEO 2026: Googlebot rendert JavaScript, KI-Crawler wie ChatGPT nicht
Googlebot holt HTML, führt JavaScript in headless Chromium aus und indexiert das Ergebnis, wenn auch langsam. Crawler von ChatGPT und anderen KI-Diensten lesen dagegen nur das reine HTML und übersehen Inhalte von Single-Page-Apps weitgehend.

The AI crawlers don't get there. ChatGPT's crawlers fetch the HTML and stop. If your app mounts into an empty <div id="root">, that empty div is the whole page as far as ChatGPT search is concerned.
We build LovedByAI, an AI search optimization platform for small businesses. Our WordPress plugin logs every request a recognised bot makes to a site, which is where the crawler numbers below come from. This post is the developer version of our breakdown of dynamic bot rendering, a CDN feature now being pitched to marketers as the fix. It includes a standard-library Python script that shows what each crawler gets from a URL, and that spots dynamic rendering from the outside. We ran it before publishing; the output below is real.
So for an SPA there are two audiences now:
AI bots come in three kinds, and they do different jobs:
The shares are from LovedByAI's logs: the average across 924 WordPress sites running our plugin, nearly three in four of them small and mid-sized businesses, from 6 September to 5 October 2026. An app with a different audience will have a different mix, but the reach is worth knowing: in that window OAI-SearchBot crawled 90.6% of the sites and ChatGPT-User 91.7%.
The one to worry about is OAI-SearchBot. OpenAI uses it to surface websites in ChatGPT search. If it reads an empty shell, ChatGPT search has little more than your <title> to go on for that URL.
The second check matters more than it looks. A crawler getting a different page than a browser is what dynamic rendering, a bot-blocking firewall and cloaking all look like from outside. The script can't tell you which one it is, only that you should go and look.
And here is a server-rendered page, our own GEO checklist (a Next.js page rendered on the server):
$ python3 bot_view.py https://www.lovedby.ai/blog/geo-checklist https://www.lovedby.ai/blog/geo-checklist agent status words h1 Browser 200 4,243 GEO Checklist: 30 On-Page Checks for AI… Googlebot 200 4,243 GEO Checklist: 30 On-Page Checks for AI… OAI-SearchBot 200 4,243 GEO Checklist: 30 On-Page Checks for AI… ChatGPT-User 200 4,243 GEO Checklist: 30 On-Page Checks for AI… GPTBot 200 4,243 GEO Checklist: 30 On-Page Checks for AI… ok: every agent gets the same page, with text in the HTML. Enter fullscreen mode Exit fullscreen mode Same 4,243 words for every agent, and the H1 in the HTML. That's the result you want.
Dynamic bot rendering is the old dynamic rendering pattern pointed at AI assistants. A rule on your CDN (CloudFront, Cloudflare or Vercel) checks the user agent for a list of assistant tokens such as chatgpt-user, claude-user and perplexity-user. Matching requests get a stored, pre-rendered snapshot of the page. Everyone else, OAI-SearchBot included, gets the live page.
To see what that looks like to the script, we built a toy version: the same demo app, plus a server that hands a pre-rendered snapshot.html to the nine assistant tokens on the published list.
ASSISTANT_TOKENS = ( " chatgpt-user ", " codex ", " claude-user ", " claude-code ", " perplexity-user ", " gemini-deep-research ", " duckassistbot ", " amzn-user ", " mistralai-user " ) ROOT = os. path. join ( os. path. dirname ( os. path. abspath ( __file__ )), " demo " ) class Handler ( http. server. SimpleHTTPRequestHandler ): def __init__ ( self, * a, ** kw ): super (). __init__ ( * a, directory = ROOT, ** kw ) def do_GET ( self ): ua = ( self. headers. get ( " User-Agent " ) or "" ). lower () if self. path in ( " / ", " /index.html " ) and any ( t in ua for t in ASSISTANT_TOKENS ): self. path = " /snapshot.html " super (). do_GET () Enter fullscreen mode Exit fullscreen mode Run against it:
Not by Google's definition, while the snapshot matches the page. Google's dynamic rendering documentation says similar content isn't cloaking, and its example of cloaking is a page about cats for users and dogs for crawlers. The risk is drift: a snapshot that still shows last month's price isn't the same content as the live page any more. When bot_view.py reports DIFFERS BY USER AGENT, compare the two responses by hand.
The same page warns that "dynamic rendering was a workaround and not a long-term solution."
Put the content in the HTML you send, for every visitor. Google recommends three ways to do that:
If you can't change the app soon, serve a prerendered copy to every bot you care about, OAI-SearchBot, PerplexityBot and Claude-SearchBot included, not just the live fetchers. And give it an expiry: re-render on deploy and when prices or stock change, so the copy never drifts from the page.
Whatever you pick, put bot_view.py in CI against a staging URL. It exits with 1 when anything is flagged:
python3 bot_view.py https://staging.example.com/ https://staging.example.com/pricing Enter fullscreen mode Exit fullscreen mode A deploy that ships an empty shell, or a CDN rule that starts sending bots a different page, then fails the build instead of quietly costing you ChatGPT search.
Mostly outside this problem. WordPress builds each page in PHP and sends finished HTML, so the headings, text and schema are in the first response. The exceptions are widgets that load after the page (some review carousels, booking and menu embeds) and headless WordPress with a front end that renders in the browser.
By default, LovedByAI doesn't switch anything by user agent: people, ChatGPT-User, OAI-SearchBot and Googlebot get the same WordPress page at the same URL. Each page's machine-readable copy lives at its own public address (the page URL plus?llm_view=1 ), linked from the site's footer with a plain HTML link, so any person or bot can open it.
The full breakdown of dynamic bot rendering, with the pros, the cons and how it compares with prerendering and server-side rendering: https://www.lovedby.ai/blog/dynamic-bot-rendering
For the business side, whether a small business should turn dynamic bot rendering on at all, see our analysis on Medium.