Zum Inhalt springen
Core Web Vitals Spezialisten
Server & Hosting

Server-Timing: wo die Zeit im Backend wirklich bleibt

Der TTFB nennt nur die Summe. Mit dem Server-Timing-Header zerlegen Sie die Serverzeit im Browser in Datenbank, Zwischenspeicher, Anwendung und Vorlage.

13 Min. Lesezeit TTFBMessungBackend

Ein hoher TTFB ist ein Befund ohne Adresse. Die Messung sagt, dass der Server 640 Millisekunden gebraucht hat, bevor das erste Byte unterwegs war – sie sagt nicht, ob die Zeit in einer Datenbankabfrage lag, in einem verfehlten Zwischenspeicher, im Zusammensetzen der Vorlage oder in einem Aufruf, der auf ein anderes System gewartet hat. Genau hier setzt der Server-Timing-Header an: Die Anwendung meldet ihre eigene Zeit in benannten Posten an den Browser, der Browser zeigt sie im Entwicklerwerkzeug und reicht sie an JavaScript weiter. Aus einer Summe werden Posten, aus einem Verdacht wird eine Messung – und zwar bei echten Besuchern, nicht nur im Prüflauf. Als guter TTFB gelten 0,8 Sekunden (web.dev) oder weniger; 44 Prozent (Web Almanac 2025) der mobilen Websites erreichten diesen Wert. Dieser Beitrag zeigt, wie der Header aufgebaut ist, wie die Werte im Server entstehen, wie man sie aus den Felddaten einsammelt und wo die Grenzen liegen. In der Server-Optimierung ist er bei uns der erste Handgriff, sobald die Antwortzeit auffällig wird.

Das Wichtigste in Kürze

  • Der TTFB ist eine Summe. Server-Timing zerlegt sie in benannte Posten, die der Server selbst meldet: Datenbank, Zwischenspeicher, Anwendungslogik, Vorlage, Wartezeit auf fremde Systeme.
  • Als guter TTFB gelten 0,8 Sekunden (web.dev) oder weniger; 44 Prozent (Web Almanac 2025) der mobilen Websites lagen 2025 in diesem Bereich.
  • Jeder Posten trägt einen Namen, dazu optional eine Dauer über dur und eine Beschreibung über desc (W3C). Fehlt die Dauer, meldet die Schnittstelle 0 (W3C) statt eines Fehlers.
  • Im Feld lesbar sind die Werte über PerformanceServerTiming: in Chrome ab Version 65 (MDN), in Firefox ab 61 (MDN), in Safari ab 16.4 (MDN). Ab Chrome 136 (Chrome for Developers) zeigt auch die Übersicht des Leistungspanels die Serverposten.
  • Der Kopf ist öffentlich und auf denselben Ursprung beschränkt; fremde Ursprünge brauchen Timing-Allow-Origin (W3C). Kurze, neutrale Namen sind deshalb Pflicht, keine Kür (MDN).

Der TTFB nennt die Summe, nicht die Posten

Der Time to First Byte misst die Spanne vom Absenden der Anfrage bis zum ersten Byte der Antwort. Als grober Richtwert gelten 0,8 Sekunden (web.dev) oder weniger als gut, schlecht wird es oberhalb von 1,8 Sekunden (web.dev). Im Feld sieht die Verteilung so aus: 44 Prozent (Web Almanac 2025) der mobilen Websites erreichten 2025 einen guten TTFB, 17 Prozent (Web Almanac 2025) lagen im schlechten Bereich, auf dem Rechner waren es 55 Prozent (Web Almanac 2025) im guten Bereich. Ein Jahr zuvor lag der mobile Anteil bei 42 Prozent (Web Almanac 2025) – es bewegt sich etwas, aber langsam. Was diese Zahlen nicht sagen: woran es liegt. Der Wert ist eine Summe aus Netzweg, Verbindungsaufbau und der Zeit, die der Server für die Antwort braucht. Wie sich diese Summe grob aufteilt, steht in der Analyse der Server-Antwortzeit; dieser Beitrag setzt eine Ebene tiefer an und teilt den Serveranteil weiter auf.

Der Serveranteil ist deshalb so unangenehm, weil er vor allem anderen liegt. Der Largest Contentful Paint gilt bis 2,5 Sekunden (web.dev) als gut, gemessen am 75. Perzentil (web.dev) der Seitenaufrufe – und der TTFB ist die erste seiner vier Phasen. Jede Millisekunde, die der Server verbraucht, wird nach vorn durchgereicht: Sie verschiebt den Start des Ladens der Hauptressource, verschiebt deren Ende und damit den Zeitpunkt, an dem der Besucher etwas sieht. Wie sich diese Kette im Einzelnen aufteilt, beschreibt der Beitrag zu den vier Phasen des LCP; wie die Kennzahl in das Gesamtbild der Core Web Vitals passt, steht dort ebenfalls. Wer das Frontend aufräumt, ohne den Serversockel anzufassen, verschiebt den Engpass, statt ihn aufzulösen.

Ein Kopf, der meldet – gemessen wird trotzdem selbst

Der Header erzeugt keine Messung. Er transportiert nur, was die Anwendung selbst erhoben hat. Wer keine Zeitpunkte um Abfrage, Zwischenspeicher und Vorlage setzt, bekommt einen leeren Kopf zurück. Der Aufwand steckt also in den Messpunkten, nicht im Transport – dafür ist er einmalig: Sind die Punkte gesetzt, liefert jede einzelne Antwort ihre Aufteilung mit, auch die an echte Besucher. Genau das unterscheidet den Header von einem Prüflauf, der nur zeigt, wie sich die Seite in einer sauberen Laborumgebung verhält.

Wie der Header aufgebaut ist

Server-Timing ist ein Antwortkopf mit einer Liste von Metriken, getrennt durch Kommata. Jede Metrik hat einen Namen; dazu kommen zwei optionale Angaben, die die Spezifikation festlegt: dur für die Dauer und desc für eine Beschreibung (W3C). Die Dauer ist ein DOMHighResTimeStamp und wird üblicherweise in Millisekunden angegeben – die Spezifikation nennt das ausdrücklich eine Empfehlung und keine erzwingbare Festlegung (W3C). Namen und Beschreibungen sollten kurz bleiben, weil jedes Zeichen im Kopf jeder einzelnen Antwort mitfährt (MDN). Ein Posten ohne Dauer ist erlaubt und trägt dann nur seinen Namen – nützlich für Zustände wie einen Treffer oder Fehlgriff im Zwischenspeicher, die keine sinnvolle Zeitangabe haben.

antwort.http
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Server-Timing: db;dur=210, cache;desc="miss";dur=40, app;dur=180, tpl;dur=95
Timing-Allow-Origin: https://www.example.com
Cache-Control: private, no-store

Der Browser übernimmt die Werte, ohne sie zu bewerten. Fehlt die Angabe dur, gibt die Schnittstelle für diesen Posten 0 (W3C) zurück – der Eintrag verschwindet also nicht, er hat nur keine Dauer. Das ist praktisch, solange man es weiß, und irreführend, sobald jemand die Posten aufsummiert und sich über die Lücke wundert. Sinnvoll ist deshalb: Zustände über desc transportieren, Dauern nur dort setzen, wo wirklich Zeit verbraucht wurde, und den Namen so wählen, dass er in einer Auswertung ohne Erklärung verständlich bleibt.

Die Posten im Server erzeugen

Die Umsetzung ist unspektakulär: Vor und nach jedem Abschnitt, der Zeit kosten kann, wird ein hochauflösender Zeitstempel genommen, die Differenz landet in einer Variablen, und kurz vor dem Senden der Antwort wird daraus der Kopf gebaut. Wichtig ist nur die Reihenfolge – der Kopf muss gesetzt sein, bevor das erste Stück des Rumpfes den Prozess verlässt. Anwendungen, die früh mit der Ausgabe beginnen, können auf die Trailer-Variante ausweichen, die die Spezifikation vorsieht; sie wird allerdings ausschließlich in den Entwicklerwerkzeugen des Browsers ausgewertet und nicht über die JavaScript-Schnittstelle (MDN). Für eine Felderhebung taugt sie damit nicht.

kategorie.php
// Zeitpunkte um die Arbeit legen, nicht um den Aufruf
$t0 = hrtime(true);
$artikel = katalog_lesen($kategorieId);
$dbMs = (hrtime(true) - $t0) / 1e6;

$t1 = hrtime(true);
$block = cache_lesen('nav:' . $sprache, $treffer);
$cacheMs = (hrtime(true) - $t1) / 1e6;

$t2 = hrtime(true);
$html = vorlage_rendern('kategorie', $artikel, $block);
$tplMs = (hrtime(true) - $t2) / 1e6;

// Kopf setzen, bevor der Rumpf beginnt
header(sprintf(
    'Server-Timing: db;dur=%.1f, cache;desc="%s";dur=%.1f, tpl;dur=%.1f, app;dur=%.1f',
    $dbMs,
    $treffer ? 'hit' : 'miss',
    $cacheMs,
    $tplMs,
    (hrtime(true) - $t0) / 1e6
));

Drei Dinge lohnen sich in der Praxis. Erstens gehören die Messpunkte um die Arbeit und nicht um den Funktionsaufruf: Eine Abfrage, die erst beim Auslesen des Ergebnisses ausgeführt wird, misst sonst null Millisekunden und verschiebt ihre Zeit in den nächsten Posten. Zweitens sollten sich die Posten nicht überlappen – sonst ist ihre Summe größer als die Zeit der Anwendung, und niemand traut den Zahlen mehr. Drittens hilft ein Gesamtposten für die Anwendungszeit beim Gegenrechnen: Bleibt zwischen ihm und der Summe der Einzelposten eine Lücke, liegt dort ungemessene Arbeit. Wie sich diese Laufzeit darunter weiter aufteilt, behandelt der Beitrag zur PHP-Laufzeit mit OPcache und Arbeitsprozessen.

Vom Werkzeug in die Felddaten

Im Labor ist der Kopf sofort sichtbar: Die Netzwerkansicht der Entwicklerwerkzeuge zeigt die gemeldeten Posten im Reiter für Zeiten, und ab Chrome 136 (Chrome for Developers) tauchen sie zusätzlich in der Übersicht des Leistungspanels auf. Interessanter ist der zweite Weg: Jede Antwort mit dem Kopf füllt die Eigenschaft serverTiming des zugehörigen Eintrags in der Performance-Schnittstelle. Verfügbar ist das in Chrome ab Version 65 (MDN), in Firefox ab 61 (MDN) und in Safari ab 16.4 (MDN) – die Abdeckung reicht damit für eine Erhebung, die nicht nur eine einzelne Browserfamilie beschreibt.

messwerte.js
const [navigation] = performance.getEntriesByType('navigation');
const posten = [];

for (const eintrag of navigation.serverTiming) {
  posten.push({
    name: eintrag.name,
    dauer: Math.round(eintrag.duration),
    hinweis: eintrag.description
  });
}

// zusammen mit dem Kontext an den eigenen Sammelpunkt
navigator.sendBeacon('/messwerte/', JSON.stringify({
  seitentyp: document.body.dataset.seitentyp,
  angemeldet: document.body.dataset.angemeldet === '1',
  ttfb: Math.round(navigation.responseStart),
  posten
}));

Der eingesammelte Wert gehört zu einem echten Seitenaufruf und lässt sich mit allem verbinden, was ohnehin erhoben wird: Seitentyp, Anmeldezustand, Region, Gerät. Damit beantwortet man Fragen, an denen ein Prüflauf scheitert – etwa, ob die Kategorieseite ausschließlich für angemeldete Nutzer langsam ist, weil dort der Seitenzwischenspeicher nicht greift. Zu beachten ist, dass die Schnittstelle in einigen Browsern auf sichere Kontexte beschränkt ist und damit HTTPS voraussetzt (MDN). Wie sich Feld- und Labormessung sinnvoll nebeneinander lesen lassen, steht im Beitrag zu Felddaten und Performance-Budgets. Und weil das Versenden der Messwerte selbst Tempo kosten kann, lohnt der Blick auf fetchLater und das verzögerte Senden von Messdaten.

Datenbank

Der häufigste Einzelposten. Ein Name je Abfragegruppe reicht: db für die Hauptabfrage, dbn als Posten ohne Dauer für die Zahl der Abfragen. Steigt diese Zahl mit der Kategorietiefe oder dem gefüllten Warenkorb, sieht man das im Feld sofort. Die Hebel dahinter beschreibt der Beitrag zur Abfrageoptimierung im Shop.

Zwischenspeicher

Ein Posten mit desc für Treffer oder Fehlgriff und einer Dauer für den Zugriff selbst. Erst damit lässt sich die Trefferquote im Feld ausrechnen, statt sie zu schätzen – und erst dann sieht man, welche Seitenarten systematisch daneben liegen. Welche Ebenen sich anbieten, zeigt der Beitrag zu Caching-Strategien.

Vorlage und Ausgabe

Die Zeit für das Zusammensetzen der Antwort. Sie wächst mit der Zahl der Bausteine und mit jedem Fragment, das erst beim Rendern nachgeladen wird. Ein eigener Posten trennt sie sauber von der Datenbeschaffung und zeigt, wenn nicht die Abfrage, sondern die Vorlage der Engpass ist.

Welche Posten sich wirklich lohnen

Fünf bis sieben Posten sind in der Regel genug. Mehr Namen machen den Kopf länger und die Auswertung unübersichtlich, ohne die Diagnose zu verbessern. Bewährt hat sich diese Aufteilung: Datenbank, Zwischenspeicher mit Zustand, Anwendungslogik, Vorlage, Wartezeit auf fremde Systeme und ein Gesamtposten als Gegenprobe. Wer ein Auslieferungsnetz vorgeschaltet hat, bekommt oft schon Posten mitgeliefert, allerdings sehr unterschiedlich: Die Erhebung des Web Almanac zeigt Netze, die den Kopf auf 100 Prozent (Web Almanac 2025) der ausgelieferten Anfragen setzen, während andere ihn erst nach ausdrücklicher Konfiguration mitgeben. Wie Auslieferungsnetz und Ursprungsserver dabei zusammenspielen, behandelt der Beitrag zu Edge-Caching für Shops; wie wir daraus ein Vorgehen machen, steht auf der Seite zur TTFB-Optimierung.

Frage aus dem BetriebNur TTFBTTFB mit Server-Timing
Wo genau liegt die Zeit?unbekanntje Posten benannt
Zwischenspeicher getroffen?nur zu vermutenals Zustand im Kopf
Nur angemeldete Nutzer betroffen?nicht trennbarüber Felddaten trennbar
Wirkung einer Änderungdie Summe verschiebt sichder betroffene Posten sinkt
Aufwand für die Einführungkeinereinmalig Messpunkte setzen
Aussage über echte Besucherja, aber ohne Ursacheja, mit Ursache

Der Kern in einem Satz

Ein TTFB ohne Aufteilung ist ein Alarm ohne Ort: Er meldet, dass etwas dauert, aber nicht was. Server-Timing macht aus dem Alarm eine Liste – und aus der Liste eine Reihenfolge, in der sich arbeiten lässt.

Grenzen, Sichtbarkeit und Fallstricke

Der Kopf ist öffentlich. Wer die Antwort sieht, sieht auch die Posten – und damit ein Stück Ihrer Innenarchitektur. Die Spezifikation weist ausdrücklich darauf hin, dass der Zugriff auf die Schnittstelle deshalb standardmäßig auf denselben Ursprung beschränkt ist und fremde Ursprünge erst über Timing-Allow-Origin freigeschaltet werden (W3C). MDN formuliert die Kehrseite direkt: Der Kopf kann sensible Angaben zur Anwendung und zur Infrastruktur preisgeben (MDN). Praktisch heißt das kurze, neutrale Namen, keine Tabellennamen, keine Rechnernamen, keine Fehlertexte. Wer mehr Tiefe braucht, liefert die ausführliche Aufteilung nur an angemeldete Mitarbeitende aus und öffentlichen Antworten die grobe – zwei Varianten desselben Kopfes, gesteuert an einer Stelle im Code.

Drei Fallstricke, die uns regelmäßig begegnen

Erstens misst der Kopf ausschließlich die Zeit innerhalb der Anwendung. Wartet eine Anfrage vorher in der Warteschlange der Arbeitsprozesse, taucht das nirgends auf; dafür braucht es einen Posten, den der Webserver selbst setzt. Zweitens beginnt die Antwort bei aktivierten frühen Hinweisen schon vor dem Ende der Verarbeitung – die reine Serverzeit ist dann über den Kopf zuverlässiger abzulesen als über den TTFB allein; wie das zusammenspielt, beschreibt der Beitrag zu frühen Hinweisen mit Status 103. Drittens fährt der Kopf auf jeder einzelnen Antwort mit: Kurze Namen und knappe Beschreibungen sind deshalb kein Schönheitsthema (MDN).

In der eigenen Anwendung anfangen

  1. Die langsamste Seitenart bestimmen: Startseite, Kategorie, Detailseite, Suche oder Warenkorb. Dort, wo der TTFB im Feld am höchsten liegt, wird zuerst gemessen.
  2. Vier Messpunkte setzen: Datenbank, Zwischenspeicher, Anwendungslogik, Vorlage. Dazu einen Gesamtposten für die Zeit der Anwendung als Gegenprobe.
  3. Den Kopf zunächst nur in einer Vorschauumgebung ausliefern und im Entwicklerwerkzeug gegenlesen: Sind die Posten plausibel, passt ihre Summe zur Anwendungszeit?
  4. Den Kopf auf der Produktivumgebung aktivieren – mit neutralen Namen und ohne Beschreibungen, die Interna verraten.
  5. Die Werte über die Performance-Schnittstelle einsammeln und zusammen mit Seitentyp, Anmeldezustand und Gerät an den eigenen Sammelpunkt senden.
  6. Nach zwei Wochen auswerten: Welcher Posten trägt am stärksten zum langsamen Viertel der Aufrufe bei? Dort beginnt die Arbeit, und dort wird nach der Änderung gegengemessen.

Der Aufwand für den ersten Durchgang liegt erfahrungsgemäß bei wenigen Stunden, weil die Messpunkte an vier bis fünf Stellen im Code sitzen und der Kopf an einer. Der Ertrag zeigt sich meist beim ersten Blick in die Felddaten: Häufig liegt der größte Posten woanders, als das Team vermutet hat – und die Optimierung, die seit Monaten geplant ist, betrifft den zweitkleinsten. Genau so beginnt bei uns eine Performance-Analyse, wenn der TTFB auffällig ist und die Ursache offen. Was danach an Stellschrauben in Frage kommt, hängt vom Befund ab; einen Überblick über die Arbeitsweise gibt die Seite zu unseren Leistungen. Steht der Serversockel, geht es im Frontend weiter – etwa damit, ungenutztes CSS zu finden und zu entfernen.

Der Unterschied zwischen "der Server ist langsam" und "die Kategorieabfrage ist der größte Posten und trifft vor allem angemeldete Nutzer" ist kein Messdetail. Es ist der Unterschied zwischen einer Diskussion und einer Aufgabe.

Projekterfahrung aus Server-Optimierungen für Shops und Portale

Quellen und Studien

Dieser Artikel basiert auf Daten von web.dev, MDN Web Docs, dem W3C, dem Web Almanac 2025 des HTTP Archive und Chrome for Developers. Die genannten Zahlen beziehen sich auf den Stand der jeweiligen Veröffentlichung.

Verwandte Artikel