Zwei Headless-Modi, ein Ruf
Bis Chrome 112 bedeutete --headless das alte Headless: ein eigener Binärpfad, der nicht wirklich Chrome war. Es hatte einen eigenen User Agent mit der Zeichenkette HeadlessChrome, keine Erweiterungsunterstützung, kein chrome.runtime, keine Plugin-Liste und einen Renderpfad, der große Teile der echten Pipeline übersprang. Es zu erkennen war ein Einzeiler.
Chrome 112 brachte das neue Headless (--headless=new, seit Chrome 132 schlicht --headless). Das ist der echte Browser, nur ohne gezeichnetes Fenster. Erweiterungen laufen, die Plugin-Liste ist gefüllt, chrome.runtime existiert, und der User Agent verrät sich nicht mehr. Damit fielen fast alle Signale weg, die der alte Modus preisgab.
Der Ruf blieb trotzdem, und größtenteils zu Recht. Das neue Headless hat die leichten Spuren beseitigt. Nicht beseitigt hat es die Schicht darunter.
Was es weiterhin verrät
SwiftShader, der Sitzungskiller
Das mit Abstand zuverlässigste Signal und das, über das die meisten stolpern. Ohne GPU — der Normalzustand eines Servers — fällt Chrome auf Software-Rasterisierung zurück, und WebGL meldet:
UNMASKED_VENDOR_WEBGL → "Google Inc. (Google)"
UNMASKED_RENDERER_WEBGL → "ANGLE (Google, Vulkan 1.3.0 (SwiftShader Device ...))"
Dieses Paar existiert auf null Endgeräten. Nicht „selten“ — null. Niemand surft mit Software-Rendering auf Instagram. Das ist kein probabilistisches Signal, das einen Score erhöht; es ist ein kategorisches, das die Sitzung beendet.
Es ist auch der Grund, warum --disable-gpu für diesen Einsatzzweck so schlechter Rat ist. Das Flag wird massenhaft aus Docker-Fehlerbehebungsthreads kopiert, wo es einen Absturz behebt — und es erzwingt genau den obigen Rückfall.
Das Fenster, das nicht da ist
Headless muss sich trotzdem ein Fenster ausdenken, und das sieht man. outerWidth und outerHeight entsprechen oft innerWidth und innerHeight, weil es keine Chrome zu berücksichtigen gibt — ein echtes Desktop-Chrome hat rund 16 Pixel horizontalen und 85 bis 90 vertikalen Unterschied. screenX und screenY stehen auf null. screen.availHeight ist gleich screen.height, was bedeutet: Auf dieser Maschine existiert nirgends eine Taskleiste oder ein Dock.
Nichts bewegt sich je
Keine Mausbewegung, keine Scroll-Trägheit, kein Fokus und Blur beim Fensterwechsel, kein visibilitychange, wenn ein Tab in den Hintergrund rückt. Cloudflare Turnstile und Vergleichbare beobachten das fortlaufend, nicht als einmalige Prüfung. Eine Sitzung mit sauberem Fingerprint und null Eingabeereignissen ist eine Sitzung, die jeden statischen Test bestanden und den einzigen dynamischen nicht bestanden hat.
Der Stapel darunter
Headless ändert nichts an TLS- oder HTTP/2-Fingerprints — das ist tatsächlich eine gute Nachricht, denn ein echter Chromium-Build liefert so oder so echte Chrome-Handshakes. Es ändert auch nichts an der CDP-Fläche, wo Automatisierung tatsächlich erwischt wird. Ein Moduswechsel bewegt weder das eine noch das andere.
Warum Stealth-Plugins nicht mehr wirken
puppeteer-extra-plugin-stealth und seine Nachfolger setzen beim Dokumentstart eine Reihe von JavaScript-Patches: webdriver verstecken, navigator.plugins füllen, chrome.runtime fälschen und so weiter. 2019 reichte das oft.
Heute nicht mehr, und zwar aus strukturellen statt aus Wartungsgründen. Jeder Patch ist ein JavaScript-Override, und JavaScript-Overrides haben eine Form. Native Getter stringifizieren als [native code]; Ersatz nicht. Prototyp-Eigenschaften erscheinen nicht als eigene Eigenschaften; Ersatz schon. Und kein Skript auf Seitenebene erreicht den Scope eines Web Workers, sodass jeder im Haupt-Thread gepatchte Wert der Kopie im Worker widerspricht.
Veröffentlichte Erkennungs-Benchmarks berichten 2026 von nahezu vollständiger Identifizierung von Sitzungen mit Stealth-Plugins — ein Anbieter nennt rohes Playwright mit 98,2 % und kommerzielle Browserless-Sitzungen im Stealth-Modus mit 100 %, bei Falsch-Positiv-Raten unter 1 %. Diese Zahlen stammen von einer Firma, die Erkennung verkauft, und sind entsprechend zu lesen — an der Richtung besteht aber kein Zweifel.
Die Plugins sind nicht schlecht geschrieben. Sie arbeiten auf einer Schicht, die die Antwort nicht ausdrücken kann.
Die Option, die nicht auf der Karte steht
Die Gegenüberstellung „headless oder headful“ verdeckt die Wahl, die wirklich funktioniert: headful Chrome auf einem virtuellen Display.
Unter Linux läuft dafür Xvfb — ein echter X-Server, der in den Speicher statt auf einen Monitor rendert. Chrome startet ganz ohne --headless-Flag. Es ist ein vollständiger Browser, der ein vollständiges Fenster zeichnet, mit echtem GPU-Rendering, wenn der Host eine GPU hat. Es gibt keinen Headless-Modus zu erkennen, weil es keinen gibt.
Xvfb :99 -screen 0 1920x1080x24 &
DISPLAY=:99 google-chrome --window-size=1920,1080
Was das behebt: das SwiftShader-Signal (mit vorhandener GPU), die Signale zur Fenstergeometrie, die fehlende Chrome, availHeight und die ganze Klasse der „es gibt kein Display“-Spuren. Was es nicht behebt: CDP-Leaks und Verhalten. Die brauchen eigene Arbeit.
Der Preis sind ein paar Dutzend Megabyte RAM je Instanz und eine Zeile im Provisioning-Skript. Verglichen damit, einen Satz Stealth-Patches gegen ein bewegliches Ziel zu pflegen, ist das nahezu gratis.
Wann Headless weiterhin richtig ist
Nichts davon macht Headless nutzlos. Es macht es zum falschen Werkzeug für eine bestimmte Aufgabe.
Headless ist in Ordnung für: interne Tests, Screenshot- und PDF-Erzeugung, das Crawlen eigener Seiten, Scraping von Seiten ohne Bot-Abwehr, CI-Pipelines, Linkprüfung, Uptime-Monitoring. Überall dort, wo niemand versucht, dich zu identifizieren, ist Headless schneller und leichter, und du solltest es nutzen.
Headless ist das falsche Werkzeug für: alles hinter Cloudflare, Akamai, DataDome oder PerimeterX; alles mit einem eingeloggten Account auf einer Plattform, der das wichtig ist; alles, wo es etwas kostet, als Automatisierung eingestuft zu werden.
Der Unterschied ist nicht technische Raffinesse. Er liegt darin, ob es einen Gegner gibt.
Eine kurze Diagnose
Wenn du wissen willst, wo dein aktuelles Setup steht: vier Prüfungen, geordnet danach, wie viel sie verraten:
- WebGL-Renderer. Lies
UNMASKED_RENDERER_WEBGL. Steht dort SwiftShader, halt an und behebe das zuerst; alles andere ist dann egal. - Fenstergeometrie.
outerHeight - innerHeightsollte im Desktop-Chrome ungefähr 85 bis 90 betragen, undscreen.availHeightsollte kleiner sein alsscreen.height. - CreepJS. Lade es und lies die Abschnitte zu Headless und Lügenerkennung. Es ist unverblümt in dem, was es findet.
- Ein echtes Ziel. Lade eine Seite, die dir wirklich wichtig ist, und schau, ob du eine Challenge bekommst. Jeder synthetische Test ist nur ein Stellvertreter für diesen.
Führ dieselben vier Prüfungen mit einem von Hand bedienten Browser auf derselben Maschine durch. Die Unterschiede zwischen beiden Listen sind deine Aufgabenliste.