AgentGrade
EnglishEspañolDeutsch日本語中文
← Wissensdatenbank

JavaScript-gerenderte Websites und Agent-Sichtbarkeit

Was heißt „JavaScript-gerendert"?

Eine Seite ist JavaScript-gerendert, wenn das HTML, das der Server sendet, im Wesentlichen leer ist — der eigentliche Inhalt (Überschriften, Text, Produktinformationen) erscheint erst, nachdem der Browser ein JavaScript-Bundle heruntergeladen und ausgeführt hat, das ein Framework wie React, Vue, Svelte oder Angular in ein leeres Root-Element mountet.

Eine typische JS-gerenderte Homepage sieht auf der Leitung so aus:

<body>
  <div id="root"></div>
  <script type="module" src="/assets/index-D9LVtTP6.js"></script>
</body>

Der sichtbare <body>-Inhalt ist null Bytes groß. Alles, was du im Browser siehst, wurde clientseitig aufgebaut, nachdem das JS gelaufen ist.

Warum das für Agenten wichtig ist

Die meisten KI-Crawler führen kein JavaScript aus. Wenn ChatGPT, Claude, Perplexity, Gemini-Fetches oder ein typisches Agenten-Framework deine URL abrufen, bekommen sie das rohe HTML — und deine Homepage ist leer. Deine Produktbeschreibung, Überschriften, Links und Texte sind für sie unsichtbar.

Das ist ein echter Schaden für Retrieval und Zusammenfassung. Ein LLM, das gefragt wird „Was macht dieses Unternehmen?" und dabei deine URL zitiert, liefert eine schlechte Antwort oder halluziniert, weil auf der Seite nichts zu lesen ist.

Was dadurch nicht kaputtgeht

Tags im statischen <head> deines HTML sind vom clientseitigen Rendering nicht betroffen. Selbst eine vollständig JS-gerenderte SPA kann diese Checks bestehen, wenn das Build-Tool den Head korrekt befüllt:

Wenn deine Website JS-gerendert ist und diese Checks ebenfalls fehlschlagen, sind das unabhängige Lücken — behebe sie, indem du die richtigen <head>-Tags in das HTML-Template deines Builds einfügst, unabhängig davon, ob du das Rendering selbst behebst.

Die gemeinsame Ursache: statisch gehostete SPA-Bundles

JS-gerenderte Websites werden meist als statisches Bundle ausgeliefert (eine index.html plus ein Ordner mit JS/CSS), deployt auf einen Host, der für jeden GET-Request die index.html ausliefert: Vercel static, Netlify, Cloudflare Pages, GitHub Pages, S3 + CloudFront, Fastly Frontend, Firebase Hosting.

Dieses Deployment-Muster hat überhaupt keine serverseitige Pro-Request-Logik. Dieselbe Konfiguration, die einen JS-gerenderten Body erzeugt, erzeugt auch:

Ein einzelnes Symptom (z. B. Content Negotiation) zu beheben, ohne das Deployment-Modell anzugehen, ist meist unmöglich — du brauchst entweder serverseitige Logik oder eine Edge Function vor dem statischen Bundle.

Die Fix-Leiter

Wähle die günstigste Option, die deinen Fall löst.

1. Prerendering zur Build-Zeit

Wenn dein Homepage-Inhalt weitgehend statisch ist, prerendere ihn während des Builds. Der Browser hydriert weiterhin mit React/Vue/etc., aber das initiale HTML des Servers enthält bereits den gerenderten DOM. Crawler sehen echten Inhalt.

Das ist der Fix mit dem geringsten Aufwand für Marketing-Seiten, Blogs und Dokumentation.

2. Vollständiges Server-Side Rendering (SSR)

Wenn die Homepage dynamische Daten braucht (Login-Status, A/B-Tests, frische Inhalte), nutze ein Framework, das bei jedem Request rendert:

SSR braucht eine Runtime (Node, Bun, Deno oder eine Edge-Runtime) — das heißt, du verlässt reines Static Hosting.

3. Edge Function für Content Negotiation

Wenn die Migration zu SSR zu disruptiv ist, setze eine Edge Function vor dein statisches Bundle, die Requests abfängt und eine alternative Repräsentation ausliefert:

// Cloudflare Worker / Vercel Edge / Netlify Edge
export default async function (request) {
  const accept = request.headers.get('accept') || '';
  const ua = (request.headers.get('user-agent') || '').toLowerCase();
  const isAgent = /claudebot|gptbot|chatgpt-user|perplexitybot|google-extended/.test(ua);

  if (isAgent || accept.includes('text/markdown')) {
    return new Response(await fetchMarkdownSummary(), {
      headers: { 'Content-Type': 'text/markdown', 'Vary': 'Accept, User-Agent' },
    });
  }

  return fetch(request); // pass through to static bundle
}

Das ist der richtige Fix, wenn die zugrunde liegende App clientseitig gerendert bleiben muss (z. B. eine stark interaktive App), Agenten aber trotzdem sinnvollen Inhalt lesen sollen.

4. <noscript>-Fallback (Minimallösung)

Nimm mindestens deine Kerninhalte in einen <noscript>-Block im statischen HTML auf:

<noscript>
  <h1>Your Company</h1>
  <p>What you do, in two sentences. Link to docs, pricing, API.</p>
  <a href="/about">About</a> · <a href="/docs">Docs</a> · <a href="/pricing">Pricing</a>
</noscript>

Das ist kein Ersatz für Prerendering, verhindert aber, dass Crawler eine komplett leere Seite sehen. Die meisten JS-gerenderten Websites haben das nicht.

So prüfst du es

Rufe deine Website ohne JavaScript-Ausführung ab und prüfe, ob echter Inhalt im Body steht:

curl -s -A "ClaudeBot/1.0" https://yourdomain.com/ | \
  python3 -c "import sys, re; t = sys.stdin.read(); body = re.search(r'<body[^>]*>(.*?)</body>', t, re.S); print(len(re.sub(r'<[^>]+>', '', body.group(1) if body else '').strip()))"

Wenn die ausgegebene Zahl unter ein paar hundert Zeichen liegt, sehen Agenten deine Inhalte nicht.

Mehr erfahren

Verwandte Themen