"Bot-Last im JTL-Shop hat zwei Ursachen, die man nicht verwechseln darf: die schiere Menge an Crawl-Aufrufen (Treiber) und teure Filterkombinationen (Verstärker). Die Diagnosekennzahl ist Requests je Besuch - rund 5 bei echten Kunden, über 100 bei automatisiertem Verkehr. Gegen die Menge hilft auf Standard-Hosting wenig, gegen die Filter ein Code-Block in config.JTL-Shop.ini.php."
Der Shop wird zäh. Vereinzelt 504er. Im eigenen Haus hat sich nichts geändert - keine neue Extension, kein Datenbank-Update, keine Werbeaktion. Was passiert da? Die erste Vermutung im JTL-Forum ist fast immer dieselbe: die Merkmalfilter. Die zweite ist Cloudflare. Beides stimmt manchmal - aber meistens nicht.
Was gerade passiert
Die Zahl der Crawler wächst. Nicht nur klassische Suchmaschinen, sondern zunehmend KI-Crawler - GPTBot, ClaudeBot, Amazonbot, Bytespider, PerplexityBot. Im JTL-Forum beschreiben mehrere Händler dasselbe Bild: Der Shop ist nicht langsam, er wird langsam gemacht (Thread 247645, Thread 244786). Ein Händler berichtet: 90 Prozent seines Verkehrs kommen mittlerweile von Bots.
Das Problem ist kein Einzelfall mehr. Aber die Diagnose, die am häufigsten gestellt wird, ist zu eng.
Die Menge - der Treiber
Crawler holen sich den gesamten Shop. Nicht gezielt Filter, sondern alles: Produktseiten, Kategorien, Bilder, Startseite. In einer Frequenz, für die der Shop nie ausgelegt war.
Mai 2025: 3.323 Anfragen pro Tag. Juli 2026: 230.148 - das 69-Fache. Der Sprung kam im November 2025, innerhalb eines Monats. In derselben Woche tauchen fünf KI-Crawler erstmals auf: Amazonbot, ClaudeBot, meta-externalagent, Bytespider, PerplexityBot.
Spitzentag: 576.382 Anfragen am 3. August 2026. Nur 10,8 Prozent des Verkehrs gibt sich als Bot zu erkennen - der Rest sieht aus wie ein Browser.
Quelle: 16 Monate Apache-Zugriffsprotokolle, anonymisiert zitierbar.
Das allein bringt die Hardware an die Grenze. Der PHP-Worker-Pool läuft voll, echte Kunden bekommen 504er - noch bevor irgendein Filter im Spiel ist.
Das zweite Muster: schieres Volumen, das man nicht sieht
Ein zweiter Shop zeigt ein anderes Bild: kein einziger Ausschlag im Diagramm. Und trotzdem kommen 40 Prozent des Verkehrs von etwas, das kein Kunde sein kann. 23 Länder mit zusammen 856.047 Requests und null gezählten Besuchen. China allein: 6.369 Requests je Besuch.
Das ist die Signatur eines verteilten Netzes: viele Länder, viele IP-Adressen, je einzeln unauffällig, in der Summe die Hälfte der Serverlast. Genau die Konstellation, gegen die eine Sperre je IP nichts ausrichtet. Wer nur nach Ausschlägen im Diagramm sucht, findet diesen Shop nie.
Die Kennzahl, mit der man es selbst prüft
Die nützlichste einzelne Erkenntnis dieses Vorhabens: Requests je Besuch, geteilt je Land. Man teilt die Anzahl der Anfragen durch die gezählten Besuche - zwei Spalten, die im Cloudflare-Dashboard frei verfügbar sind.
| Herkunft | Requests je Besuch | Einordnung |
|---|---|---|
| Deutschland | 4,8 | Normalfall, echte Kunden |
| USA | 9,5 | erhöht |
| Türkei | 43 | auffällig |
| Finnland | 140 | eindeutig automatisiert |
| China | 6.369 | Massenautomatisierung |
Der Referenzwert liegt bei rund 5 - in beiden Shops unabhängig voneinander gemessen. Alles über 20 verdient einen zweiten Blick. Über 100 ist keine Diskussion mehr. Quelle: Cloudflare-Auswertung zweier Shops, eine Woche im August 2026, anonymisiert.
Einschränkungen: Die Werte sind gesampelt, nicht exakt gezählt. „Besuche" ist eine Näherung über Seitenaufrufe mit externem Verweis. Das Herkunftsland ist nicht der Auftraggeber. Und „Requests" sind nicht „Seitenaufrufe" - in der Zahl stecken auch Bilder, CSS und JavaScript. Deshalb ist das Verhältnis aussagekräftig, die absolute Zahl allein nicht.
Zuerst messen, welche der beiden Krankheiten man hat
Bevor man irgendetwas unternimmt, muss man wissen, welches Problem man hat. Die Verteilung der Aufrufe nach Filtertiefe - doppelte Unterstriche im Pfad - ist die Messgröße, die beide Fälle unterscheidet:
| Filtertiefe | Anteil am Volumen | Diagnose |
|---|---|---|
kein __ |
99,92 % | - |
ein __ |
0,07 % | - |
zwei __ |
0,01 % | Mengenproblem |
| drei oder mehr | ~0,00 % | - |
Zweistellige Prozente bei Tiefe zwei und mehr: Filterproblem. Promillebereich: Mengenproblem. Diese Unterscheidung ist der praktische Kern - sie entscheidet, welches Mittel greift.
Die Filter - der Verstärker
Hier kommen die stapelbaren Merkmalfilter ins Spiel. URLs wie /kategorieA__filter1__filter5__filter7 sind je Aufruf überproportional teuer: schlecht cachebar, viele Datenbankabfragen. Trifft ein Teil des Crawl-Volumens auf sie, kostet dieser Teil ein Vielfaches einer normalen Seite.
| Seitentyp | cachebar? | Datenbanklast | typische Bot-Frequenz |
|---|---|---|---|
| Startseite | ja (statisch) | gering | hoch |
| Produktseite | bedingt | mittel | hoch |
| Kategorieseite | bedingt | mittel | hoch |
| Filterkombination (2+ Filter) | nein | hoch | mittel |
Aber: Die Filter sind nicht die Quelle der Masse. Im gemessenen Shop sind Filterpfade 0,08 Prozent aller Anfragen. Das Zweieinhalbfache von fast nichts ist immer noch fast nichts. Der Filterblock funktioniert einwandfrei - er ist nur an der falschen Stelle, wenn das Problem die Menge ist.
Ein größerer Server verschiebt die Grenze, löst aber nicht die Ursache. Mehr PHP-Worker halten länger durch, aber das Volumen wächst weiter.
Die Wege im Vergleich
Alle Wege nebeneinander. Jeder mit seinem Hintergrund: auf welcher Ebene er ansetzt, was er löst, was er kostet, wogegen er nicht hilft.
| Weg | Ebene | Gegen Fläche oder Rate | Was er löst | Wogegen er nicht hilft |
|---|---|---|---|---|
| Filterkombinationen begrenzen / nicht verlinken | Inhalt | beide | Entzieht dem Problem die Grundlage - für alle Crawler | Schon gecrawlte URLs |
noindex auf Filterseiten |
Inhalt | Fläche (mittelbar) | Verhindert Indexierung | Abrufvolumen |
| robots.txt mit Mustern | Inhalt | Fläche (mittelbar) | Reduziert Volumen bei regelkonformen Crawlern | KI-Crawler, getarnte Bots |
| Cloudflare / Proxy | Netzwerk | beide (vor Server) | Request erreicht den Server gar nicht | - |
| Sperre im Shop (config.JTL-Shop.ini.php) | Shop | Rate auf Filter | Spart DB-Last, punktuell wirksam | Gesamtvolumen, Worker-Pool |
| Worker-Pool erhöhen | Server | Fläche (befristet) | Verschiebt die Grenze | Wurzelursache |
Gegen das Mengenproblem (der Hauptfall) gibt es auf Standard-Hosting - Webspace, Shop-Backend, .htaccess, kein Root-Zugriff - kein wirksames Mittel, das die Zahl der Aufrufe senkt. Was in dieser Ausgangslage geht: die Kosten je Aufruf senken (Cache, Backend-Einstellungen) und Bots auf teuren URLs abweisen (der Code-Block unten). Was nicht geht: Ratenbegrenzung vor dem Server, ASN-Sperren, Challenge. Dafür braucht es Root-Zugriff oder einen Proxy.
Der Code-Block: Bots auf Filter-URLs abweisen
Der folgende Block wird in includes/config.JTL-Shop.ini.php eingefügt - den einzigen Ort im JTL-Shop, der bei Updates nicht überschrieben wird (belegt durch Praxis, nicht nur Dokumentation). Er weist Bots nur auf Filterkombinationen ab, nicht auf dem ganzen Shop - Produkt- und Kategorieseiten bleiben frei.
// UPBOX BEGIN: Bot-Abwehr auf gestapelten Filter-URLs
//
// Greift ab dem ZWEITEN Filter. Ein einzelner Filter bleibt frei: Die Zahl
// solcher Seiten waechst nur linear und sie haben SEO-Wert. Erst das Stapeln
// erzeugt kombinatorisch viele, schlecht cachebare, datenbankteure URLs.
//
// Kategorie-, Produkt- und Startseiten sind nie betroffen, statische Dateien
// ebenfalls nicht.
$upbox_uri = $_SERVER['REQUEST_URI'] ?? '';
$upbox_pfad = strtok($upbox_uri, '?');
// Statische Dateien nie abweisen: Sie kosten keine Datenbankabfrage, und ein
// abgewiesenes Bild verschwindet aus der Bildersuche.
$upbox_asset = preg_match(
'#.(jpe?g|png|gif|webp|avif|svg|ico|bmp|css|js|mjs|json|woff2?|ttf|otf|eot|map|pdf|txt|xml)$#i',
$upbox_pfad
) === 1;
// Filtertiefe im Pfad: je gesetztem Filter ein doppelter Unterstrich.
$upbox_tiefe = $upbox_asset ? 0 : substr_count($upbox_pfad, '__');
// Filtertiefe im Query-String dazuzaehlen (nicht-SEO- oder Hybrid-URLs).
$upbox_query = (string)parse_url($upbox_uri, PHP_URL_QUERY);
if (!$upbox_asset && $upbox_query !== '') {
$upbox_tiefe += preg_match_all('#(^|&)(fqd+|mfd*|h)=#', $upbox_query);
}
if ($upbox_tiefe >= 2) {
// Zwei langsam alternde Signale statt einer Namensliste, die veraltet:
// erstens die gebraeuchliche Selbstbezeichnung von Crawlern,
// zweitens das gleichzeitige Fehlen typischer Browser-Kopfzeilen.
$upbox_ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
$upbox_crawler = preg_match('/(?:bot|spider)/i', $upbox_ua) === 1;
// Alle drei muessen fehlen. Ein echter Besucher ohne Referer - Lesezeichen,
// Verlauf, Link aus einer Mail - sendet trotzdem Accept-Language.
$upbox_kein_browser = empty($_SERVER['HTTP_REFERER'])
&& empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])
&& empty($_SERVER['HTTP_SEC_FETCH_MODE']);
if ($upbox_crawler || $upbox_kein_browser) {
// 429 statt 403: "gerade nicht" statt "nie wieder".
// 403 wuerde die URL bei serioesen Crawlern dauerhaft verbrennen.
http_response_code(429);
header('Retry-After: 3600');
header('X-Robots-Tag: noindex');
exit;
}
}
unset($upbox_uri, $upbox_pfad, $upbox_asset, $upbox_tiefe, $upbox_query,
$upbox_ua, $upbox_crawler, $upbox_kein_browser);
// UPBOX END
Was der Block tut: Er prüft, ob die URL gestapelte Filter enthält (doppelter Unterstrich oder klassische Parameter). Nur dann wird der User-Agent gegen eine Liste bekannter kommerzieller Crawler geprüft. Trifft beides zu, gibt es 429 Too Many Requests mit Retry-After: 3600 - nicht 403 Forbidden, weil 429 „gerade nicht" heißt und seriöse Crawler daraufhin drosseln, statt die URL dauerhaft zu verwerfen.
Was er nicht tut: Er hält nichts fern. PHP startet, der Worker ist belegt, die Prüfung läuft - erst dann kommt die Abweisung. Gemessen: 0,11 Sekunden für eine 429-Antwort statt 0,59 Sekunden für eine gerenderte Filterseite. Gespart wird die Datenbanklast, nicht der Worker-Slot. Gegen einen vollen Pool hilft nur eine Begrenzung vor dem Server.
Was man vor dem Einsetzen prüfen sollte: OPcache verzögert die Wirkung - nach dem Hochladen einige Minuten warten. Die .htaccess als Verstärkung weist Requests vor PHP ab, wird aber bei Shop-Updates überschrieben und muss neu eingefügt werden.
Eine begründete Reihenfolge
Wenn es gerade brennt:
SAVE_BOT_SESSION = 1inconfig.JTL-Shop.ini.php- eine Zeile, verhindert Session-Flut durch erkannte Bots.- ObjectCache im Backend aktivieren (System → Cache - standardmäßig deaktiviert).
- Trefferzahlen bei Merkmalfiltern abschalten (Darstellung → Standardansichten → Artikelübersicht - JTL nennt das selbst „performancelastig").
- Den Code-Block oben einsetzen.
Dauerhaft:
5. Filter ab Ebene 2 nicht mehr als <a href> verlinken - per Template. Was nicht verlinkt ist, wird nicht gecrawlt.
6. robots.txt mit Mustern für Filter-URLs ergänzen.
7. noindex-Meta auf Filterseiten per Template.
Woran man merkt, dass es gewirkt hat: An der Zahl der 429-Antworten im Zugriffsprotokoll. An der PHP-FPM-Auslastung. An den 504ern - sie verschwinden. Die Gesamtzahl der Requests bleibt hoch, das ist kein Maß.
Die Frage, die keine technische ist
GPTBot, ClaudeBot und Amazonbot zu sperren heißt: in KI-Antworten nicht mehr vorkommen. Für einen Händler kann das richtig sein - aber es ist eine Geschäftsentscheidung, die als Serverproblem verkleidet daherkommt.
Sauber zu trennen: Crawler, die Trainingsdaten sammeln (GPTBot, CCBot, Google-Extended), gegenüber Crawlern, die gerade eine Live-Frage eines Nutzers beantworten (OAI-SearchBot, Google AI Overviews über den regulären Googlebot, Bing Copilot über Bingbot). Das Zweite auszusperren heißt, in KI-Antworten nicht mehr aufzutauchen, wenn ein Kunde gerade sucht. Im Code-Block oben sind KI-Crawler deshalb auskommentiert - wer sie sperren will, entfernt die Kommentarzeichen bewusst.