Warum TTFB die LCP deines Shops bestimmt
Time to First Byte ist die Zeit zwischen dem Senden einer HTTP-Anfrage und dem Empfang des ersten Bytes der HTML-Antwort. Bei einem Magento 2 Shop bedeutet ein hoher TTFB, dass der Browser buchstäblich wartet, bevor er auch nur ein Pixel rendern kann. LCP kann nicht besser sein als TTFB plus die Renderzeit des LCP-Elements.
Googles CrUX-Schwellenwert für TTFB liegt bei 800 Millisekunden für 'needs improvement'. Über 1800 Millisekunden befindet man sich im roten Bereich. Bei Hypernode-Shops ohne konfigurierten Full-Page-Cache sehe ich TTFB-Werte von 1,2 bis 2,4 Sekunden regelmäßig. Das ist kein Frontend-Optimierungsproblem: keine Menge an JavaScript-Bundling oder Bildkomprimierung hilft, wenn der Server 1,8 Sekunden zum Antworten benötigt.
Frontend-Performance ist das Thema der magento-performance Seite. Was ich hier behandle, ist die Schicht darunter: der Server-Stack, der bestimmt, wie schnell Magento HTML generiert oder aus dem Cache liefert.
Der Magento Server-Stack: vom Request bis zum HTML
Eine Magento 2 Anfrage durchläuft mehrere Schichten, bevor HTML zurückkommt. Jede Schicht kann ein Engpass sein.
- ▸Nginx: Reverse Proxy und Static-File-Server. Normalerweise kein TTFB-Problem, aber falsche worker_processes oder Keepalive-Einstellungen kosten Zeit.
- ▸PHP-FPM: der PHP Process Manager. Zu wenige Workers bedeutet, dass Anfragen in der Warteschlange landen. Zu viele Workers auf einem kleinen Server verbraucht Speicher und verursacht Swap.
- ▸OPcache: speichert kompilierten PHP-Bytecode im Speicher. Ohne OPcache kompiliert Magento bei jeder Anfrage zehntausende PHP-Dateien neu. Mit OPcache, aber zu wenig memory_consumption, verliert es ständig Cache-Einträge.
- ▸Redis (Full-Page-Cache): speichert die vollständige HTML-Ausgabe von Seiten. Ein Cache-Hit liefert in 5 bis 30 Millisekunden statt 800ms bis 2 Sekunden für eine PHP-generierte Antwort.
- ▸Redis (Session): speichert Benutzersitzungen. Bei stark frequentierten Shops konkurriert dies mit dem FPC um Redis-Speicher, wenn keine zwei separaten Redis-Instanzen konfiguriert sind.
- ▸Varnish: HTTP-Accelerator vor Nginx. Auf Hypernode via Sentia-Konfiguration verfügbar. Liefert gecachete Antworten bei nahezu null Millisekunden server-seitig.
Der TTFB eines nicht optimierten Magento 2 Shops ist typischerweise die kombinierte PHP-FPM und MySQL Zeit. Jede Cache-Schicht, die korrekt konfiguriert wird, entfernt einen Schritt aus dieser Kette.
Was ich abstimme und wie
Ich arbeite in einer festen Reihenfolge: messen, Engpass isolieren, anpassen, über CrUX oder PSI-Lab verifizieren. Typische Abstimmpunkte pro Schicht:
- ▸PHP-FPM pm.max_children: berechnet aus verfügbarem RAM geteilt durch durchschnittlichen PHP-Prozessspeicher (oft 80 bis 120 MB pro Worker bei Magento). Zu niedrig bedeutet Warteschlange, zu hoch bedeutet Swap.
- ▸OPcache opcache.memory_consumption: standardmäßig 256 MB auf Hypernode. Für große Magento-Installationen mit vielen Modulen ist 512 MB realistischer. Ich messe über opcache_get_status(), wie viele Einträge verloren gehen.
- ▸Redis Full-Page-Cache: maxmemory basierend auf Shop-Größe setzen. Eviction-Policy auf allkeys-lru, damit die meistbesuchten Seiten im Cache bleiben. Zwei separate Instanzen für FPC und Sessions.
- ▸Redis maxmemory-policy prüfen: volatile-lru verliert Session-Keys nicht unerwartet, allkeys-lru verwirft alles unter Druck. Die Wahl hängt davon ab, ob FPC und Sessions geteilt sind.
- ▸Varnish VCL: Hit-Ratio via varnishstat messen. Typische Probleme sind Cookies, die das Caching blockieren, falsche Vary-Header oder zu niedriges default_ttl für Produktseiten.
- ▸MySQL Slow-Query-Log: Abfragen über 1 Sekunde identifizieren. Magento generiert bei Produktlistenseiten ohne Index auf category_id kombiniert mit status viele langsame Abfragen.
- ▸MySQL innodb_buffer_pool_size: muss groß genug sein, um den Hot-Datensatz im Speicher zu halten. Disk-Reads bei jeder Abfrage sind der stille TTFB-Killer.
Ich stimme nicht blind alle Einstellungen auf Maximum ab. Jede Anpassung hat einen Grund und ich messe den Effekt vorher und nachher. Bei einem 2 GB Hypernode-Plan sind 20 PHP-FPM Workers korrekt; bei einem 4 GB Plan sind 35 realistischer.
Hypernode-spezifische Einschränkungen und wie ich damit umgehe
Hypernode ist eine Managed-Plattform. Das bedeutet gute Defaults und zuverlässige Infrastruktur, aber auch Einschränkungen, die man auf einem selbstverwalteten VPS nicht hat.
- ▸Kein Root-Zugang: PHP-FPM und Nginx können nicht über systemd neu gestartet werden. Konfigurationsänderungen über die hypernode-manage-vhosts CLI oder das Hypernode-Control-Panel.
- ▸OPcache-Abstimmung: Einige OPcache-Parameter können über php.ini überschrieben werden, andere nicht. Ich teste, welche Parameter auf dem jeweiligen Hypernode-Plan wirksam sind.
- ▸Varnish über Sentia-Konfiguration: Aktivierung über das Control Panel. Die VCL ist begrenzt anpassbar, aber die Standard-Magento 2 VCL deckt 80 Prozent der Fälle ab.
- ▸Redis: Zwei Redis-Instanzen auf Hypernode Professional und höher verfügbar. Bei Starter-Plänen gibt es eine gemeinsame Instanz; dann trenne ich FPC und Sessions über separate Datenbanken (db 0 und db 1).
- ▸MySQL: Keine direkte innodb_buffer_pool_size-Anpassung ohne Hypernode-Support. Ich optimiere über Indizes und Query-Umschreibung statt Server-Parameter.
Ich arbeite täglich mit Hypernode in der Produktion und kenne die Grenzen der Plattform. Ich empfehle keine Abstimmung außerhalb der Plattformmöglichkeiten und täusche dich nicht mit Versprechen, die Hypernode technisch nicht erlaubt.
-- Kunden-Case
CarCare24 auf Hypernode: TTFB von 974ms auf 390ms (CrUX)
CarCare24 ist ein Magento 2 Shop mit etwa 3400 Produkten auf Hypernode, mit Cloudflare Free. Die CrUX-Daten zeigten einen TTFB von 974 Millisekunden auf Mobilgeräten, was in den 'needs improvement'-Bereich fällt. Der Full-Page-Cache lief auf Redis, war aber unzureichend konfiguriert: zu wenig maxmemory, keine separate Instanz für Sessions und eine Varnish-Hit-Ratio von 62 Prozent.
Nach der Trennung der Redis-Instanzen für FPC und Sessions, Erhöhung von maxmemory auf der FPC-Instanz und Korrektur der Varnish VCL (Vary-Header-Bereinigung, Cookie-Whitelist für Cloudflare) stieg die Hit-Ratio auf 91 Prozent. Der CrUX-TTFB fiel auf 390 Millisekunden: grün. PHP-FPM Workers wurden von 8 auf 14 auf dem 2 GB Plan angepasst, nach Messung des durchschnittlichen Speicherverbrauchs pro Worker. Der kombinierte Effekt: die Homepage-LCP sank mit, da der Browser das HTML früher empfängt und früher mit dem Laden des LCP-Elements beginnen kann.
Was ich auf Server-Ebene nicht mache
Transparenz über das, was außerhalb meines Scopes liegt, verhindert Enttäuschungen.
- ✕Kernel-Level TCP-Abstimmung (BBR, Socket-Buffer): kein Root auf Hypernode, nicht möglich und nicht meine Verantwortung.
- ✕MySQL Slave-Replikation oder Read-Replica-Setup: dies erfordert DBA-Expertise und separate Infrastruktur außerhalb des Hypernode-Standards.
- ✕PHP-FPM über systemd auf Shared-Hosting ohne FPM-Zugang konfigurieren: auf Hosts, die PHP als Apache-Modul betreiben, ist FPM-Abstimmung nicht verfügbar.
- ✕Garantien auf spezifische TTFB-Ziele: ich gebe einen realistischen Erwartungsbereich basierend auf Hosting-Plan und Shop-Konfiguration, keine vertraglichen Ziele.
- ✕Vollständige Server-Migration zu anderem Hosting: das ist ein separates Projekt und fällt außerhalb des Geschwindigkeits-Abstimmungsscopes.
Für Frontend-Performance (Hero-Image-Optimierung, Drittanbieter-Skripte, Hyvä, JavaScript-Bundling) verweise ich auf die magento-performance Seite. Server-seitige und Frontend-Performance sind komplementäre, aber getrennte Projekte.
Scope und Preis
Der Umfang eines server-seitigen Abstimm-Projekts hängt vom Hosting-Plan, der aktuellen Cache-Konfiguration und davon ab, wie weit der TTFB von einem akzeptablen Niveau entfernt ist. Ein Shop auf Hypernode Starter mit nur Redis-Fehlkonfiguration ist ein kürzeres Projekt als ein Shop auf Shared-Hosting ohne FPM-Zugang, bei dem auch eine Hosting-Migration anfällt.
Preise auf Anfrage. Ich sende ein Angebot auf Basis einer ersten technischen Bestandsaufnahme. Diese Bestandsaufnahme ist kostenlos, wenn du danach ein Gespräch mit mir führst.