Zum Hauptinhalt springen

Leitfaden zur Leistungsoptimierung

Ein tiefgehender Einblick in Techniken zur Webleistungsoptimierung, von Core Web Vitals bis hin zu fortschrittlichen Caching-Strategien.

James Mitchell James Mitchell
11 Min. Lesezeit
Illustration: ein Wasserfalldiagramm des Seitenladevorgangs mit dem Largest Contentful Paint unter 2,5 Sekunden markiert

Web-Performance wirkt sich direkt auf Nutzererfahrung, Konversionsraten und Suchmaschinen-Rankings aus. Langsame Seiten verlieren Besucher, bevor diese Ihre Inhalte überhaupt sehen, und jede zusätzliche Sekunde Wartezeit untergräbt das Vertrauen. So machen Sie Ihre Website schnell und halten sie schnell.

Dieser Leitfaden geht von der Messung aus: Zuerst legen Sie fest, was „schnell“ bedeutet, dann beheben Sie die größten Verursacher (Bilder, Schriften, JavaScript), und schließlich sichern Sie die Verbesserungen mit Caching und Monitoring dauerhaft ab.

Core Web Vitals

Die Core Web Vitals von Google – Largest Contentful Paint (LCP), Interaction to Next Paint (INP) und Cumulative Layout Shift (CLS) – liefern messbare Richtwerte für die Nutzererfahrung. Streben Sie einen LCP unter 2,5 Sekunden, einen INP unter 200 Millisekunden und einen CLS unter 0,1 an. INP hat im März 2024 First Input Delay (FID) als Core Web Vital abgelöst und ist eine deutlich strengere Kennzahl: FID maß nur die Verzögerung, bis die erste Interaktion verarbeitet wurde, während INP die Latenz aller Interaktionen während des gesamten Besuchs betrachtet.

Die Schwellenwerte werden am 75. Perzentil der Seitenaufrufe bewertet, getrennt nach Mobilgeräten und Desktop. Dieses Detail ist wichtig. Eine Seite, die auf Ihrem Entwicklungs-Laptop im Büro-WLAN schnell ist, kann trotzdem durchfallen, wenn ein Viertel Ihrer echten Besucher mit Android-Smartphones der Mittelklasse in einem überlasteten Netz unterwegs ist.

Was Ihnen jede Kennzahl wirklich sagt

  • LCP misst, wann das größte Bild oder der größte Textblock im Viewport fertig gerendert ist. Meist ist das ein Hero-Bild, eine Überschrift oder ein langer Absatz. Ein schlechter LCP wird am häufigsten durch eine langsame Serverantwort, Render-blockierendes CSS und JavaScript oder ein Hero-Bild verursacht, das der Browser zu spät entdeckt.
  • INP misst, wie schnell die Seite nach einem Klick, Tippen oder Tastendruck sichtbar reagiert. Ein schlechter INP bedeutet fast immer, dass der Main Thread ausgelastet ist: lange JavaScript-Tasks, aufwendige Hydration oder teure Event-Handler.
  • CLS misst unerwartete Bewegungen sichtbarer Inhalte. Die üblichen Verdächtigen sind Bilder und Einbettungen ohne Abmessungen, Webfonts, die mit abweichenden Metriken eingewechselt werden, sowie Banner oder Anzeigen, die oberhalb bestehender Inhalte eingefügt werden.

Labordaten versus Felddaten

Labor-Tools wie Lighthouse führen einen einzelnen synthetischen Ladevorgang auf einem simulierten Gerät aus. Sie eignen sich hervorragend zur Fehlersuche und um Regressionen in einem Pull Request zu erkennen, können INP aber nicht direkt messen, weil niemand mit der Seite interagiert. Felddaten, die von echten Besuchern erhoben werden, sind die eigentliche Grundlage der Core-Web-Vitals-Bewertung. Nutzen Sie Labordaten, um Probleme zu finden, und Felddaten, um zu entscheiden, welche Probleme zählen.

Beim Server anfangen

Kein noch so ausgefeiltes Frontend-Tuning kann ein langsames erstes Byte kaschieren. Time to First Byte (TTFB) ist kein Core Web Vital, liegt aber direkt vor dem LCP: Der Browser kann erst mit dem Rendern beginnen, wenn das HTML ankommt.

  • Rendern Sie vor, was Sie können. Statische Generierung liefert HTML direkt aus einem CDN ohne Umweg über den Ursprungsserver. Für Seiten, die tatsächlich Daten pro Anfrage brauchen, cachen Sie die gerenderte Ausgabe mit kurzer Lebensdauer am Edge, statt jede Anfrage von Grund auf neu zu rendern.
  • Streamen Sie HTML. Wenn serverseitiges Rendern unvermeidbar ist, senden Sie den Dokumentkopf frühzeitig, damit der Browser bereits CSS und Schriften laden kann, während der Body noch erzeugt wird.
  • Vermeiden Sie Weiterleitungsketten. Jede Weiterleitung ist ein vollständiger Roundtrip, bevor überhaupt Inhalte laden. Verlinken Sie direkt auf kanonische URLs und halten Sie die Regeln für abschließende Schrägstriche und www einheitlich.
  • Aktivieren Sie moderne Protokolle und Komprimierung. HTTP/2 oder HTTP/3 sowie Brotli- oder gzip-Komprimierung für Text-Assets sind für jeden ernstzunehmenden Host oder jedes CDN Grundausstattung.

Bildoptimierung

Bilder machen auf den meisten Websites nach wie vor den größten Teil des Seitengewichts aus. Verwenden Sie moderne Formate wie WebP und AVIF mit passenden Fallbacks. Setzen Sie responsive Bilder mit srcset ein, um jedem Gerät passend dimensionierte Bilder zu liefern. Laden Sie Bilder unterhalb des sichtbaren Bereichs mit dem nativen Attribut loading="lazy" verzögert.

Ein sauber aufgebautes responsives Bild sieht so aus:

<img
  src="/images/dashboard-800.webp"
  srcset="/images/dashboard-400.webp 400w,
          /images/dashboard-800.webp 800w,
          /images/dashboard-1600.webp 1600w"
  sizes="(min-width: 1024px) 50vw, 100vw"
  width="1600"
  height="900"
  alt="Revenue dashboard showing monthly trends"
  loading="lazy"
  decoding="async"
/>

Mit den Attributen width und height kann der Browser das korrekte Seitenverhältnis reservieren, bevor die Datei heruntergeladen ist, was eine häufige Ursache für Layoutverschiebungen beseitigt. Das Attribut sizes teilt dem Browser mit, wie breit das Bild dargestellt wird, damit er den kleinsten geeigneten Kandidaten aus srcset wählen kann.

Das LCP-Bild anders behandeln

Lazy Loading ist die falsche Wahl für das Bild, das am wahrscheinlichsten Ihr LCP-Element ist. Ein verzögert geladenes Hero-Bild wartet auf das Layout, bevor der Download beginnt, was den LCP direkt verzögert. Für das Hero-Bild gilt:

  • Entfernen Sie loading="lazy".
  • Fügen Sie fetchpriority="high" hinzu, damit der Browser es vor anderen Bildern lädt.
  • Stellen Sie sicher, dass es im initialen HTML auffindbar ist und nicht als CSS-Hintergrund gesetzt oder per JavaScript eingefügt wird.
  • Muss es aus CSS oder einer spät ladenden Komponente stammen, fügen Sie ein <link rel="preload" as="image"> in den Dokumentkopf ein.

Wenn Sie Astro verwenden, erzeugen die eingebauten Komponenten <Image /> und <Picture /> optimierte Formate, Abmessungen und srcset-Werte zur Build-Zeit, was Ihnen den Großteil der manuellen Buchführung abnimmt.

Schriften ohne Flackern

Webfonts beeinflussen vor allem LCP und CLS. Eine Schrift, die das Rendern von Text blockiert, verzögert den LCP, wenn das LCP-Element Text ist, und eine Fallback-Schrift, die gegen einen Webfont mit abweichenden Metriken getauscht wird, verursacht Layoutverschiebungen.

  • Hosten Sie Ihre Schriften selbst, damit sie vom selben Ursprung und über dieselbe Verbindung wie Ihre Seite kommen und ein zusätzlicher DNS-Lookup und TLS-Handshake entfällt.
  • Reduzieren Sie Schriften auf die Zeichensätze, die Sie tatsächlich verwenden. Ein reines Latin-Subset ist nur ein Bruchteil so groß wie eine vollständige mehrsprachige Datei.
  • Verwenden Sie ausschließlich WOFF2; jeder moderne Browser unterstützt es.
  • Laden Sie nur die ein oder zwei Schriftdateien per Preload, die im sichtbaren Bereich verwendet werden.
  • Gleichen Sie die Fallback-Metriken an – mit den Deskriptoren size-adjust, ascent-override und descent-override, sodass der Wechsel nahezu unsichtbar ist.
@font-face {
  font-family: "Inter Fallback";
  src: local("Arial");
  size-adjust: 107%;
  ascent-override: 90%;
}

body {
  font-family: "Inter", "Inter Fallback", system-ui, sans-serif;
}

Die genauen Override-Werte hängen vom Schriftpaar ab; mehrere Open-Source-Werkzeuge können sie für Sie berechnen.

Code Splitting

Liefern Sie nur das JavaScript aus, das eine Seite tatsächlich braucht. Frameworks wie Astro erledigen das dank der Island-Architektur automatisch, doch bei clientlastigen Anwendungen verringern dynamische Importe und routenbasiertes Splitting die initiale Bundle-Größe erheblich.

Die Bytes sind nur die halbe Rechnung. JavaScript muss außerdem auf dem Main Thread geparst, kompiliert und ausgeführt werden, und auf einem Mittelklasse-Smartphone kann das um ein Vielfaches länger dauern als auf einem Entwicklerrechner. Deshalb zeigen sich große Bundles als schlechter INP und nicht nur als langsames Laden.

Interaktiven Code bei Bedarf laden

Verschieben Sie alles, was für die erste Interaktion nicht nötig ist. Eine Diagrammbibliothek, die in einem Modal verwendet wird, gehört nicht ins initiale Bundle:

const openReport = async () => {
  const { renderChart } = await import("./charts/revenue-chart");
  renderChart(document.querySelector("#report")!);
};

button.addEventListener("click", openReport);

Wählen Sie in Astro die Hydration-Direktive bewusst. client:load hydriert sofort, client:idle wartet, bis der Browser im Leerlauf ist, und client:visible wartet, bis die Komponente in den sichtbaren Bereich scrollt. Ein Kommentar-Widget am Ende eines Artikels braucht so gut wie nie client:load.

Ihre Abhängigkeiten prüfen

Lassen Sie bei jedem Produktions-Build einen Bundle-Analyzer laufen und halten Sie Ausschau nach den üblichen Verdächtigen: eine komplette Utility-Bibliothek, die für eine einzige Funktion importiert wird, Datumsbibliotheken, die jede Locale mitbündeln, doppelte Kopien desselben Pakets in unterschiedlichen Versionen und Polyfills für Browser, die Sie nicht mehr unterstützen. Eine einzige schwere Abhängigkeit zu ersetzen spart oft mehr als wochenlange Mikrooptimierung.

Den Main Thread freihalten

INP-Probleme sind Main-Thread-Probleme. Jede Aufgabe, die länger als 50 Millisekunden läuft, ist ein Long Task, und während sie läuft, kann der Browser nicht auf Eingaben reagieren.

  • Teilen Sie lange Tasks auf. Zerlegen Sie große Schleifen und Initialisierungsroutinen in Abschnitte und geben Sie die Kontrolle zwischendurch an den Browser zurück. scheduler.yield() ist in Chromium-basierten Browsern und in Firefox verfügbar; prüfen Sie per Feature-Detection, ob es vorhanden ist, und greifen Sie andernfalls auf setTimeout zurück.
  • Tun Sie weniger in Event-Handlern. Aktualisieren Sie die Oberfläche sofort und verschieben Sie Analytics-Aufrufe, Speichervorgänge und nicht sichtbare Arbeit.
  • Vermeiden Sie Layout Thrashing. Wer Layout-Eigenschaften wie offsetHeight liest, nachdem Styles geschrieben wurden, erzwingt ein synchrones Layout. Bündeln Sie Lesezugriffe vor den Schreibzugriffen.
  • Verlagern Sie aufwendige Berechnungen vom Main Thread. Das Parsen großer JSON-Payloads, das Filtern großer Datensätze oder Bildverarbeitung können in einem Web Worker laufen.
  • Behalten Sie Skripte von Drittanbietern im Blick. Tag-Manager, Chat-Widgets und A/B-Testing-Tools verursachen häufig die schlimmsten Long Tasks einer Seite. Laden Sie sie, nachdem die Seite interaktiv ist, und hinterfragen Sie, ob jedes einzelne seinen Preis noch wert ist.
async function processInChunks<T>(items: T[], work: (item: T) => void) {
  for (let i = 0; i < items.length; i++) {
    work(items[i]!);
    if (i % 100 === 0) {
      await new Promise((resolve) => setTimeout(resolve, 0));
    }
  }
}

CSS und Rendering

Stylesheets im Dokumentkopf blockieren das Rendern konstruktionsbedingt. Das ist meist richtig, denn Sie möchten nicht, dass ungestylte Inhalte aufblitzen, bedeutet aber, dass die CSS-Größe den LCP direkt beeinflusst.

  • Halten Sie das kritische CSS klein. Utility-First-Frameworks, die nur die tatsächlich verwendeten Klassen erzeugen, helfen dabei.
  • Vermeiden Sie @import innerhalb von CSS-Dateien, da es sequenzielle Anfrageketten erzeugt.
  • Verwenden Sie content-visibility: auto für lange Abschnitte unterhalb des sichtbaren Bereichs, damit der Browser die Renderarbeit überspringen kann, bis sie sich dem Viewport nähern. Kombinieren Sie es mit contain-intrinsic-size, um Sprünge der Scrollleiste zu vermeiden.
  • Animieren Sie transform und opacity statt Eigenschaften wie top, width oder height, die bei jedem Frame ein Layout auslösen.

Caching-Strategien

Setzen Sie auf einen mehrschichtigen Caching-Ansatz: Browser-Cache für statische Assets mit langen max-age-Headern, CDN-Cache am Edge für geografisch verteilte Inhalte und Anwendungs-Cache für berechnete oder datenbankgestützte Daten. Verwenden Sie Cache-Busting mit Content-Hashes für unveränderliche Assets.

Ein praxistauglicher Satz an Headern deckt die meisten Websites ab:

# Gehashte Build-Assets: ändern sich nie, für immer cachen
Cache-Control: public, max-age=31536000, immutable

# HTML: immer neu validieren, damit Deployments sofort sichtbar sind
Cache-Control: public, max-age=0, must-revalidate

# Halbdynamische API-Antworten: veraltete Version ausliefern, während aktualisiert wird
Cache-Control: public, max-age=60, stale-while-revalidate=300

Die zentrale Regel lautet: Nur Dateien mit Content-Hash erhalten lange Lebensdauern. Wenn ein Dateiname für unterschiedliche Inhalte wiederverwendet werden kann, lässt ein langes max-age Nutzer nach einem Deployment auf alten Versionen sitzen.

Caching am Edge und in der Anwendung

Cachen Sie auf CDN-Ebene HTML für anonyme Besucher, wann immer der Inhalt es zulässt, und nutzen Sie Surrogate Keys oder Tag-basiertes Purging, sofern Ihr Anbieter das unterstützt, damit eine Inhaltsaktualisierung genau die betroffenen Seiten invalidiert. Cachen Sie auf Anwendungsebene teure Abfragen und berechnete Fragmente und legen Sie die Invalidierung ausdrücklich fest. Ein Cache ohne klare Invalidierungsstrategie liefert irgendwann falsche Daten aus – und das ist schlimmer, als langsame Daten auszuliefern.

Die nächste Seite beschleunigen

Sobald die aktuelle Seite schnell ist, sorgen Sie dafür, dass sich die Navigation verzögerungsfrei anfühlt. Laden Sie wahrscheinliche nächste Seiten beim Überfahren mit der Maus oder beim Eintritt von Links in den Viewport vorab; Astro enthält genau dafür eine konfigurierbare Prefetch-Funktion. Stellen Sie sicher, dass Seiten für den Back/Forward-Cache des Browsers infrage kommen, indem Sie unload-Handler und Cache-Control: no-store bei HTML vermeiden, das es nicht braucht.

Ein Performance-Budget festlegen

Ohne Leitplanken kehren Verbesserungen wieder um. Ein Performance-Budget macht aus „schnell halten“ Zahlen, an denen ein Build scheitern kann:

  • Maximales komprimiertes JavaScript pro Route.
  • Maximales Gesamtgewicht der Bilder auf wichtigen Landingpages.
  • Mindestwert für den Lighthouse-Performance-Score auf repräsentativen Seiten.
  • Feldziele: LCP, INP und CLS am 75. Perzentil innerhalb der Schwellenwerte für „gut“.

Prüfen Sie die laborbasierten Budgets bei jeder Änderung in Ihrem lokalen oder CI-Gate und überprüfen Sie die Feldziele in regelmäßigen Abständen. Wird ein Budget überschritten, behandeln Sie es wie einen fehlschlagenden Test: Beheben Sie entweder die Regression oder treffen Sie eine ausdrückliche, dokumentierte Entscheidung, das Limit anzuheben.

Monitoring

Leistungsoptimierung ist keine einmalige Aufgabe. Richten Sie Real User Monitoring (RUM) ein, um Leistungskennzahlen aus echten Nutzersitzungen zu erfassen. Kommerzielle RUM-Dienste oder die Open-Source-JavaScript-Bibliothek web-vitals geben Ihnen fortlaufend Einblick, wie echte Nutzer Ihre Website erleben.

Felddaten selbst zu erheben erfordert nur wenige Zeilen:

import { onCLS, onINP, onLCP } from "web-vitals";

function send(metric: { name: string; value: number; id: string }) {
  navigator.sendBeacon("/api/vitals", JSON.stringify(metric));
}

onCLS(send);
onINP(send);
onLCP(send);

Segmentieren Sie die Daten nach Möglichkeit nach Seitentemplate, Gerätetyp und Verbindung. Ein Durchschnitt über die gesamte Website verbirgt genau das eine Template, das für die meisten seiner Besucher durchfällt. Der Attribution-Build der Bibliothek kann außerdem melden, welches Element der LCP-Kandidat war und welche Interaktion einen langsamen INP verursacht hat – so wird aus einem vagen „Die Website ist langsam“ eine konkrete Korrektur.

Abschließende Gedanken

Schnelle Websites sind selten das Ergebnis eines einzelnen cleveren Tricks. Sie entstehen aus einer kurzen Liste von Gewohnheiten, die konsequent angewendet werden: mit echten Nutzerdaten messen, HTML schnell ausliefern, die LCP-Ressource priorisieren, weniger JavaScript ausliefern, den Main Thread freihalten, bewusst cachen und die Ergebnisse mit einem Budget absichern. Beginnen Sie auf Ihren wichtigsten Seiten mit der Kennzahl, die am weitesten von ihrem Schwellenwert entfernt ist, beheben Sie die größte Ursache, messen Sie erneut und wiederholen Sie den Vorgang.

Diesen Artikel teilen
performance frontend