100/100 Core Web Vitals, Fallstudie 2026

100/100 Core Web Vitals, Fallstudie 2026

Zuletzt überprüft: 29. August 2026
14 Min. Lesezeit
Leitfaden
Core Web Vitals

Der Sprung auf einen grünen Score hat in diesem Projekt nicht beim Hero-Bild angefangen, sondern bei zwei Dingen, die im WordPress-Backend niemand sieht: beim Critical CSS und bei der Reihenfolge, in der die Schriften geladen werden. Beides klingt nach Feinschliff. In der Praxis entscheidet es darüber, ob der Browser nach dem ersten Byte sofort etwas zeichnen darf oder ob er auf ein Stylesheet wartet, das er noch gar nicht braucht.

Der Kunde war ein Shop im Bereich Wohnaccessoires, ein Markt mit teurem Traffic und Besuchern, die auf dem Handy in der Bahn scrollen. Wir nennen ihn nicht beim Namen, und wir erfinden auch keine Erfolgsgeschichte darum herum. Was dieser Beitrag beschreibt, ist die Technik: welche Eingriffe wir gemacht haben, warum sie wirken und wo sie an Grenzen stoßen.

Die Ausgangslage im Lighthouse-Lab auf Mobile:

  • Mobiler Score: 42/100
  • LCP: 4,8s
  • INP: 450ms
  • CLS: 0,25

Nach vier Wochen Umbau lag derselbe Testlauf bei 100/100, LCP bei 1,2s, INP bei 48ms, CLS bei 0,00. Wichtig dabei: der Score ist ein Laborwert mit gedrosselter CPU und simuliertem 4G. Er ist reproduzierbar, aber er ist nicht die Wahrheit über echte Nutzer. Die Wahrheit steht im CrUX-Datensatz und im eigenen RUM, und die hinkt dem Deploy 28 Tage hinterher, weil das Feldfenster rollierend ist. Wer nach drei Tagen den Chrome-UX-Report aufruft und nichts sieht, hat nicht schlecht optimiert, sondern zu früh geschaut.

#Warum Critical CSS bei WordPress fast immer daneben liegt

Jedes Caching-Plugin bietet heute Critical CSS an. WP Rocket, FlyingPress, Perfmatters in Kombination mit einem Generator, Autoptimize mit dem externen Dienst von criticalcss.com. Das Versprechen ist überall gleich: die Regeln, die für den ersten sichtbaren Bildschirm nötig sind, wandern inline in den Head, der Rest wird asynchron nachgeladen. Der Effekt auf First Contentful Paint ist real.

Das Problem ist die Granularität. Die meisten Setups erzeugen genau zwei bis drei Varianten: eine für die Startseite, eine für Beiträge, eine für Seiten. Ein Shop hat aber Produktseiten mit Varianten-Auswahl, Kategorieseiten mit Filterleisten, einen Warenkorb und eine Kasse, und jede dieser Ansichten hat einen anderen Above-the-Fold-Bereich. Wenn die Produktseite das Critical CSS der Startseite bekommt, passiert das Schlimmste, was passieren kann: das Layout baut sich erst falsch auf und springt dann zurecht, sobald das vollständige Stylesheet da ist. Man tauscht Ladezeit gegen Layoutverschiebung, und CLS ist die Metrik, die Nutzer am stärksten als Schlamperei wahrnehmen.

Wir haben die Generierung deshalb an den Template-Typ gebunden, nicht an die URL. Der Cache-Key setzt sich aus dem WordPress-Template zusammen, das gerade greift, plus einem Flag für eingeloggt oder nicht, plus dem Viewport-Bucket. Konkret bedeutet das sechs statt drei Varianten, und die Produktseite bekommt Regeln für die Galerie, den Preisblock und den Variantenselektor, nicht für einen Slider, den sie nie rendert.

Zwei Fallen, die in dem Projekt Zeit gekostet haben:

  1. Elementor und ähnliche Builder erzeugen pro Seite eigene CSS-Dateien unter wp-content/uploads/elementor/css/. Ein Critical-CSS-Generator, der nur das Theme-Stylesheet kennt, übersieht diese Dateien komplett, und die Seite blitzt trotzdem unformatiert auf. Die Dateien müssen entweder in die Analyse einbezogen oder per post_id zusammengeführt werden.
  2. Der Generator läuft gegen die gerenderte Seite, nicht gegen den Quelltext. Wenn ein Consent-Banner den Viewport überdeckt, hält der Headless-Browser das Banner für den Above-the-Fold-Inhalt und schreibt dessen Regeln ins Critical CSS, während der eigentliche Seiteninhalt fehlt. Der Generator braucht eine Session, in der das Banner bereits akzeptiert oder unterdrückt ist.

Der Rest des Stylesheets kommt per <link rel="preload" as="style" onload="this.rel='stylesheet'"> nach, mit einem <noscript>-Fallback. Der Trick ist alt und funktioniert weiterhin, hat aber eine unangenehme Eigenschaft: ohne JavaScript bleibt nur der Fallback, und wer den vergisst, liefert Nutzern mit blockiertem JS eine nackte HTML-Seite aus.

#Schriften: die Reihenfolge ist wichtiger als die Dateigröße

Webfonts sind in deutschen Projekten doppelt heikel, technisch und rechtlich. Technisch zuerst.

Eine Schrift wird vom Browser erst angefordert, wenn er weiß, dass sie gebraucht wird, also nachdem er das CSS geparst und den passenden Text im DOM gefunden hat. Diese Kette kostet zwei Roundtrips, bevor überhaupt ein Byte der Schriftdatei fließt. <link rel="preload" as="font" type="font/woff2" crossorigin> bricht die Kette auf, aber nur für die Schnitte, die im ersten Bildschirm wirklich vorkommen. Wer alle sechs Schnitte einer Familie preloaded, verstopft die Verbindung genau in dem Moment, in dem das LCP-Bild sie bräuchte. In diesem Projekt waren es zwei Schnitte: Regular und Semibold, beide im Subset Latin plus deutsche Umlaute.

Das crossorigin-Attribut ist keine Zierde. Schriften werden immer im anonymen CORS-Modus geholt. Fehlt das Attribut am Preload, lädt der Browser die Datei zweimal, einmal für den Preload und einmal für die tatsächliche Verwendung. Das ist einer der häufigsten Fehler in fremden Setups, und er ist im Netzwerk-Tab in fünf Sekunden zu sehen.

Bei font-display haben wir uns für optional entschieden, nicht für swap. swap zeigt sofort die Systemschrift und tauscht später, das erzeugt genau die Verschiebung, die man vermeiden will. optional gibt der Schrift ein sehr kurzes Zeitfenster; kommt sie nicht rechtzeitig, bleibt es bei der Systemschrift, für diesen Seitenaufruf endgültig. Der Preis ist ehrlich zu nennen: manche Besucher sehen die Markenschrift beim ersten Besuch gar nicht. Wer das nicht akzeptieren kann, nimmt swap und gleicht die Metriken mit size-adjust, ascent-override und descent-override an die Fallback-Schrift an, sodass der Tausch keine Zeilenumbrüche verschiebt. Beides sind gültige Wege, aber es sind verschiedene Kompromisse, und man sollte wissen, welchen man gewählt hat.

Subsetting lohnt sich messbar. Eine vollständige WOFF2-Datei mit allen Sprachen liegt schnell bei über 100 KB; auf Latin plus die deutschen Sonderzeichen reduziert, bleiben oft unter 25 KB übrig. Werkzeuge dafür sind pyftsubset aus fonttools oder glyphhanger. Bei unicode-range gilt: es spart nur dann, wenn die Zeichen wirklich nicht vorkommen, und ein einziges Anführungszeichen im falschen Block holt die zweite Datei doch noch.

#Was die DSGVO an Drittanbieter-Skripten erzwingt

Der Punkt, an dem deutsche Projekte sich von englischsprachigen Performance-Guides unterscheiden: Drittanbieter-Skripte dürfen hier nicht einfach schnell gemacht werden, viele von ihnen dürfen vor der Einwilligung gar nicht laufen.

Google Fonts von der Google-CDN einzubinden, ist seit dem Urteil des Landgerichts München I vom 20. Januar 2022 (Az. 3 O 17493/20) ein bekanntes Risiko, weil dabei die IP-Adresse des Besuchers ohne Einwilligung an einen Drittanbieter übertragen wird. Die praktische Konsequenz ist banal und hilft der Performance: Schriften lokal ausliefern. Damit entfällt gleichzeitig ein DNS-Lookup, ein TLS-Handshake und eine fremde Verbindung, die ohnehin nicht mehr geteilt wird, seit Browser ihren HTTP-Cache pro Origin partitionieren. Der alte Vorteil einer geteilten Google-Fonts-Cache existiert seit Chrome 86 nicht mehr.

Für alles, was Informationen auf dem Endgerät speichert oder ausliest, verlangt § 25 TDDDG (bis Mai 2024 TTDSG) eine Einwilligung, unabhängig davon, ob personenbezogene Daten im Spiel sind. Das betrifft Google Tag Manager in fast jeder realen Konfiguration, Facebook Pixel, Hotjar, Chat-Widgets und A/B-Test-Tools. Technisch heißt das: diese Skripte stehen beim ersten Rendern nicht im Weg, weil sie noch gar nicht existieren dürfen.

Das ist die Pointe, die in Performance-Artikeln selten steht. Ein sauber umgesetztes Consent-Management verbessert die Kernmetriken, weil es den Hauptthread beim ersten Aufruf leer hält. Verschlechtert wird er erst danach, wenn der Nutzer zustimmt und ein Consent-Manager wie Borlabs Cookie, Complianz oder Usercentrics alle freigegebenen Skripte in derselben Millisekunde nachlädt. Genau dieser Moment fällt oft mit der ersten Interaktion zusammen, und genau dort misst INP.

Unsere Reihenfolge nach der Zustimmung sieht deshalb so aus: das Consent-Skript selbst ist klein, lokal gehostet und blockiert nichts; die freigegebenen Tags werden gestaffelt eingehängt, Messung zuerst, Chat und Marketing danach, jeweils in requestIdleCallback mit einem Timeout als Notbremse. Wer requestIdleCallback ohne Timeout benutzt, wartet auf Handys mit Dauerlast unter Umständen sehr lange.

Partytown haben wir für die Tags eingesetzt, die keinen direkten DOM-Zugriff brauchen. Die Bibliothek verschiebt Skripte in einen Web Worker und leitet Zugriffe auf document und window synchron über einen Service Worker zurück. Das entlastet den Hauptthread wirklich, aber es ist kein Schalter, den man blind umlegt:

  • Skripte, die zur Ladezeit Elemente vermessen oder Overlays einhängen, verhalten sich im Worker anders oder brechen.
  • Der Proxy braucht einen Service Worker und damit HTTPS und eine saubere Scope-Konfiguration; in Subdirectory-Installationen ist das eine typische Fehlerquelle.
  • Jeder proxied Zugriff kostet Latenz. Bei einem Tag, das hunderte Male pro Sekunde auf das DOM greift, ist der Worker langsamer als der Hauptthread.

Ein serverseitiger Tagging-Ansatz löst dieselbe Aufgabe anders, verlagert aber die rechtliche Prüfung, statt sie zu erledigen: die Einwilligungspflicht bleibt bestehen, wenn weiterhin auf dem Endgerät gespeichert oder gelesen wird.

#LCP: das Bild ist oft nur das Symptom

Largest Contentful Paint wird gern als Bildproblem behandelt. Der erste Blick sollte trotzdem der Server-Antwortzeit gelten, denn TTFB ist der Sockel, auf dem alles andere steht. Ein LCP von 2,5s ist unerreichbar, wenn das erste Byte nach 1,4s kommt.

In diesem Projekt war der Hero tatsächlich ein Teil des Problems: ein Revolution Slider, der vor dem ersten sichtbaren Bild mehrere Megabyte JavaScript und eigene Stylesheets nachzog. Ersetzt wurde er durch ein statisches CSS-Grid mit einem einzigen Bild. Dazu fetchpriority="high" auf genau diesem Bild, loading="eager" statt lazy, und AVIF als Format mit WebP als <source>-Fallback im <picture>-Element. Ein Header-Bild, das als PNG bei rund 800 KB lag, liegt als AVIF im niedrigen zweistelligen KB-Bereich, bei einer Qualitätsstufe, die auf einem Retina-Display nicht auffällt.

Drei Details, die in der Praxis über den Rest entscheiden:

  1. loading="lazy" auf dem LCP-Element ist ein sicherer Weg, LCP zu ruinieren. Viele Plugins setzen das Attribut pauschal auf alle Bilder. Die Ausnahme für das erste Bild muss explizit konfiguriert werden.
  2. srcset ohne passendes sizes lädt auf dem Handy die Desktop-Variante. Der Browser rät sonst 100vw, und das ist bei einem halbbreiten Bild doppelt so viel wie nötig.
  3. Preload-Hints konkurrieren. Wer Schrift, Hero-Bild und ein Video-Poster gleichzeitig preloaded, verteilt die Bandbreite gleichmäßig auf drei Ressourcen, von denen nur eine das LCP-Element ist.

Für CLS gilt derselbe nüchterne Blick: width und height als Attribute an jedem Bild, damit der Browser das Seitenverhältnis kennt, bevor die Datei da ist, plus aspect-ratio in CSS für alles, was per JavaScript nachgeladen wird, inklusive Werbeplätze und eingebetteter Bewertungs-Widgets. Ein reservierter leerer Kasten sieht kurz unschön aus und ist trotzdem besser als ein Text, der unter dem Finger wegspringt.

#INP in WooCommerce: der Warenkorb ist der Engpass

Bei einem Shop liegt die teuerste Interaktion fast immer beim Hinzufügen zum Warenkorb. WooCommerce löst das klassisch über admin-ajax.php, und dieser Endpunkt ist per Definition nicht cachebar: jede Anfrage bootet WordPress vollständig, lädt alle aktiven Plugins und geht durch die Datenbank. Bei einem Shop mit dreißig Plugins sind das schnell mehrere hundert Millisekunden, in denen der Button nichts tut und der Nutzer ein zweites Mal klickt.

Was geholfen hat, ohne den Shop umzubauen:

  • Optimistische UI. Der Button wechselt sofort in den Zustand “hinzugefügt” und rollt zurück, falls die Antwort einen Fehler meldet. Das verändert die gemessene Interaktionslatenz, weil INP die Zeit bis zum nächsten Frame misst, nicht die Dauer des Requests.
  • Die Arbeit aufteilen. Lange Tasks im Event-Handler werden mit await scheduler.yield() oder einem setTimeout(0) unterbrochen, damit der Browser dazwischen zeichnen darf.
  • Object Caching mit Redis, damit wp_options, Menüs und Terms nicht bei jedem AJAX-Aufruf aus MySQL kommen. Wer viel autoloaded, sollte vorher SELECT SUM(LENGTH(option_value)) FROM wp_options WHERE autoload='yes' laufen lassen; über etwa 800 KB wird es schmerzhaft, und einzelne Plugins legen dort ganze Logs ab.
  • Fragment Caching für den Warenkorb-Zähler. Das ist der Grund, warum viele Shops auf jeder Seite einen unhandlichen AJAX-Aufruf absetzen. Wer den Zähler stattdessen clientseitig aus einem Cookie oder aus dem localStorage füllt, spart den Request komplett.

#Speculation Rules, und wann sie schadet

Die Speculation Rules API ist der Teil, der sich für Nutzer am spektakulärsten anfühlt: der Browser rendert die nächste Seite im Hintergrund vollständig vor, der Klick zeigt sie ohne wahrnehmbare Ladezeit. Die Regeln stehen in einem JSON-Block im Head, eagerness steuert, wie früh der Browser loslegt: moderate reagiert auf Hover, conservative erst auf den Mousedown.

Der Haken, den Performance-Beiträge gern weglassen: eine prerenderte Seite ist eine vollständige Seite. Sie feuert ihre Analytics, sie belegt Speicher, und sie erzeugt Serverlast für Besuche, die nie stattfinden. Auf einem Shop mit langen Kategorielisten und einem Nutzer, der mit der Maus über zwanzig Produktkarten fährt, ist das ein spürbarer Unterschied in der Origin-Last. Deshalb: eagerness konservativ setzen, Warenkorb, Kasse und alles hinter einem Login per "where": {"not": ...} ausschließen, und in der Messung die Document Visibility berücksichtigen, damit vorgerenderte Seiten nicht als Aufrufe gezählt werden. WooCommerce-Aktionen, die per GET ausgelöst werden, gehören ebenfalls auf die Ausschlussliste, sonst legt ein Prerender Artikel in den Warenkorb, den niemand angeklickt hat.

#Server und Cache, und wo Shared Hosting endet

Ein vollständig grüner Score auf einem geteilten Hosting-Paket ist nicht unmöglich, aber unwahrscheinlich, und der Grund ist selten die CPU. Es ist die Kombination aus fehlendem persistentem Object Cache, einem PHP-Worker-Limit, das unter parallelen AJAX-Aufrufen sofort in die Warteschlange läuft, und einer MySQL-Instanz, die man mit Nachbarn teilt.

Das Setup, das in diesem Projekt getragen hat:

  • Redis als Object Cache, persistent, mit einem sauberen Flush beim Deploy. Ohne diesen Flush serviert man nach einem Plugin-Update serialisierte Objekte einer alten Klassenversion, und das äußert sich als Fatal Error an Stellen, die mit dem Update nichts zu tun haben.
  • Full-Page-Cache vor PHP, entweder über Nginx FastCGI Cache oder auf der CDN-Ebene. Entscheidend sind die Ausnahmen: Warenkorb, Kasse, Konto und alles mit Session-Cookie.
  • Edge-Auslieferung des HTML, damit TTFB nicht von der geografischen Distanz zum Ursprung dominiert wird. Für eine deutschsprachige Zielgruppe ist ein Ursprung in Frankfurt oder Nürnberg oft schon die halbe Miete, und dann geht es nur noch um die Cache-Trefferquote.
  • HTTP-Header, die stimmen. Cache-Control: immutable für versionierte Assets, Brotli statt gzip, und Vary-Header nur dort, wo sie wirklich nötig sind. Ein Vary: Accept auf HTML zerlegt die Trefferquote am Edge, und genau das passiert, wenn Bildformat-Aushandlung falsch konfiguriert ist.

#Was wir bewusst weggelassen haben

Nicht jede bekannte Maßnahme ist in diesem Projekt gelandet, und das ist Teil des Ergebnisses.

Kein aggressives JavaScript-Delay auf alle Skripte. Der Trick, sämtliches JS erst bei der ersten Nutzerinteraktion zu starten, hebt Lighthouse-Werte zuverlässig und verschiebt die Arbeit exakt in den Moment, den INP misst. Im Feld kann das schlechter sein als vorher.

Kein Wechsel des Page Builders. Der Umbau von Elementor auf ein Block-Theme wäre technisch der sauberste Weg gewesen, hätte aber die Redaktion des Kunden ausgebremst. Stattdessen haben wir die Builder-CSS-Dateien zusammengeführt und ungenutzte Widgets deaktiviert.

Keine Minifizierung um jeden Preis. Bei Brotli-komprimierten Dateien ist der Zusatzgewinn durch aggressives Minifying klein, das Risiko durch kaputte Inline-Handler dagegen real.

#Geschäftliche Wirkung, ohne erfundene Zahlen

Über Umsatz- und Conversion-Effekte schreiben wir hier nichts, weil wir sie in diesem Projekt nicht sauber isoliert haben. In einem laufenden Shop ändern sich parallel Sortiment, Preise, Saison und Kampagnen, und wer einen Traffic-Anstieg allein der Performance zuschreibt, verkauft eine Korrelation als Beweis.

Belegbar ist der Mechanismus. Schnellere Auslieferung senkt die Abbruchwahrscheinlichkeit während des Wartens, weil weniger Zeit zum Abbrechen bleibt. Ein stabiles Layout verhindert Fehlklicks, die im Checkout als Frustration enden. Und die Core Web Vitals sind ein bestätigter, wenn auch schwacher Rankingfaktor: sie entscheiden selten über die Position, sie werden aber zum Unterschied, wenn zwei Ergebnisse inhaltlich gleichauf liegen.

Wer den Effekt wirklich belegen will, braucht eine Messung vor und nach dem Eingriff, im Feld, über mindestens einen vollen CrUX-Zyklus, idealerweise mit einem eigenen RUM-Skript, das LCP, INP und CLS pro Template erhebt. Alles darunter ist eine Meinung mit Nachkommastellen.

Fazit: Ein grüner Score ist kein Selbstzweck, und er ist auch kein Plugin-Kauf. Er entsteht aus einer Handvoll Entscheidungen über Reihenfolge: was zuerst geladen wird, was warten darf, was erst nach einer Einwilligung existiert und was gar nicht erst auf die Seite gehört.

Verliert Ihr WooCommerce-Shop Kunden an lange Ladezeiten? WPPoland optimiert Core Web Vitals mit Messung vor und nach dem Eingriff.

Nächster Schritt

Machen Sie aus dem Artikel eine echte Umsetzung

Dieser Block stärkt die interne Verlinkung und führt Nutzer gezielt zum nächsten sinnvollen Schritt im Service- und Content-System.

Soll das Thema auf Ihrer Website umgesetzt werden?

Wenn Core Web Vitals, Rendering oder WordPress-Overhead das Problem sind, setze ich einen klaren Optimierungsplan auf und implementiere ihn.

Relevanter Cluster

Weitere WordPress-Dienste und Wissensbasis entdecken

Stärken Sie Ihr Unternehmen mit professionellem technischen Support in den Kernbereichen des WordPress-Ökosystems.

Artikel-FAQ

Häufig gestellte Fragen

Praktische Antworten zur Umsetzung des Themas.

SEO-readyGEO-readyAEO-ready3 Q&A
Haben Sie WP Rocket verwendet?#
Ja, aber als Basis. Der 100er-Score erforderte benutzerdefinierten Code für die Generierung von 'Critical CSS' auf dynamischen Produktseiten und Speculation Rules für Prefetching.
Wie haben Sie INP bei WooCommerce behoben?#
Der AJAX 'In den Warenkorb'-Button war der Engpass. Wir haben ihn so refaktoriert, dass er einen Web Worker (Partytown) verwendet, um den Hauptthread im Leerlauf zu halten.
Ist das auf Shared Hosting möglich?#
Extrem schwierig. Dieses Ergebnis stützte sich auf Redis für Object Caching und ein CDN für Edge Caching. Die TTFB (Time to First Byte) von Shared Hosting ist normalerweise zu langsam für einen 100er-Score.

Sie brauchen ein FAQ für Branche und Zielmarkt? Wir erstellen eine Version passend zu Ihren Business-Zielen.

Kontakt aufnehmen

Ähnliche Artikel

Zu viele WordPress-Plugins

Eine Versicherungsvergleichsseite kam mit über 30 Plugins, einer 705 MB großen Datenbank und einem LCP von 7.7s zu uns. Der größte Übeltäter war ein Aufrufzähler, der bei jedem Seitenaufruf in wp_postmeta schrieb. Ein echter Teardown des Plugin-Überladungsmusters, das schnelle und KI-gestützte Builds immer wieder erzeugen.