Zum Hauptinhalt springen

Devlog · Phase 0 · 2/7

Der Stack von aubia.dev: Octane, FrankenPHP und Inertia SSR

Eine um das Fünf- bis Zehnfache geringere Latenz dank Octane und FrankenPHP, ein Inertia-SSR, das das vollständige HTML ab dem ersten Byte ausliefert: der Stack von aubia.dev, Baustein für Baustein.

Veröffentlicht am 20 Juli 20265 Min. Lesezeit
  • laravel
  • octane
  • frankenphp
  • inertia
  • vite

aubia.dev liefert sein vollständiges HTML ab dem ersten Byte aus und antwortet mit einer Latenz, die im Vergleich zu einem klassischen PHP-FPM um das Fünf- bis Zehnfache geringer ist. Für eine Landingpage ist das kein Luxus: Eine Website hat eine einzige, nicht verhandelbare Aufgabe, nämlich gesehen zu werden. Von den Besuchern, aber zuerst von den Suchmaschinen und den KI-Antwortmaschinen. Ich öffne Ihnen den Stack, mit der Überlegung hinter jedem Baustein.

Serverseitiges Rendering als SEO-Invariante

Eine klassische React-Anwendung liefert eine leere HTML-Hülle zurück und füllt die Seite dann per JavaScript. Ein Browser kommt damit zurecht. Ein Crawler oder eine Antwortmaschine hingegen muss den Inhalt direkt im HTML der ersten Antwort finden.

Aus diesem Grund habe ich mir das reine Client-Rendering auf dieser Website verboten. Das SSR bestimmt hier alles Übrige. Inertia v3 rendert die React-Seiten serverseitig, und das vollständige HTML geht ab dem ersten Byte hinaus: Titel, Untertitel, Abschnitte, SEO-Metadaten, alles ist vorhanden, bevor auch nur eine Zeile JavaScript clientseitig ausgeführt wird. Die Hydratation übernimmt anschließend für die Interaktivität. Diese Regel, der Inhalt muss im ursprünglichen HTML vorhanden sein, diktiert den gesamten übrigen Frontend-Code, bis hin zum Verbot, Inhalt über ein useEffect anzuzeigen.

Octane und FrankenPHP: der Server, der nicht neu startet

Ich habe Octane vom ersten Tag des Repos an installiert, Ende April 2026, noch bevor ich eine einzige Zeile Inhalt geschrieben hatte. Das ist eine Grundsatzentscheidung, keine nachträglich hinzugefügte Optimierung. Im traditionellen PHP bootet jede Anfrage die Anwendung neu: Laden des Frameworks, Auflösung des Containers, dann die Antwort. Laravel Octane ändert dieses Modell. Die Anwendung startet einmal, dann bedienen persistente Worker Tausende Anfragen im selben Prozess.

Blieb die Wahl des Treibers. Ich habe mir RoadRunner und Swoole angesehen, die beiden anderen Octane-Optionen, bevor ich mich für FrankenPHP entschieden habe: Es bringt den Caddy-Server nativ mit, mit HTTP/3, automatischen Zertifikaten und Streaming ohne zusätzliche Schicht zu betreiben, und seine Roadmap ist im Laravel-Ökosystem aktiv.

// config/octane.php
'server' => env('OCTANE_SERVER', 'roadrunner'),
# .env : le driver actif sur aubia.dev
OCTANE_SERVER=frankenphp

Auf diesem Fundament bleibt der Laravel-Bootstrap im Speicher, und die Latenz pro Anfrage sinkt um das Fünf- bis Zehnfache gegenüber dem Cold Start von PHP-FPM. Eine niedrige und stabile Server-Antwortzeit zählt für den Performance-Score und damit für das Ranking. Doch dieses Modell verlangt eine Disziplin, auf die ich weiter unten komme.

Animierter Vergleich: php-fpm bootet das Framework bei jeder Anfrage neu und stirbt dann, während Octane einmal bootet und die aufeinanderfolgenden Anfragen in einem persistenten Worker bedient.

Inertia v3: React ohne separate API

Was diesen Stack angenehm macht, ist Inertia. Es schlägt die Brücke zwischen Laravel und React, ohne dass ich eine separate REST-API bauen und versionieren muss. Die Controller geben React-Seiten mit ihren Props zurück, so wie sie Blade-Views zurückgeben würden. Das SSR wird in der Konfiguration aktiviert, ohne einen eigenen Node-Server, der in der Entwicklung gewartet werden müsste.

// config/inertia.php
'ssr' => [
    'enabled' => true,
    'url' => 'http://127.0.0.1:13714',
],

Konkret ersetzt Inertia::render() die Views, die Routen bleiben auf der Laravel-Seite definiert, und React kümmert sich nur um das Rendering. Eine einzige Quelle der Wahrheit für das Routing, keine Verdopplung der Logik zwischen einem Back- und einem Frontend, die einander ignorieren.

Vite 8, Rolldown und React 19

Auf der Build-Seite verwendet aubia.dev Vite 8 mit Rolldown, dem nativ integrierten, in Rust geschriebenen Bundler. React 19 wird mit dem React Compiler kompiliert, der die Komponenten ohne manuelle Annotationen automatisch memoisiert. Die Aufteilung des Bundles wird explizit gesteuert, um die großen Abhängigkeiten in ihre eigenen Chunks zu isolieren:

// vite.config.ts
codeSplitting: {
    minSize: 20_000,
    groups: [
        { name: 'react', test: /node_modules\/(react|react-dom|scheduler)\//, priority: 50 },
        { name: 'motion', test: /node_modules\/(motion|motion-dom)\//, priority: 40 },
        { name: 'fontawesome', test: /node_modules\/@fortawesome\//, priority: 30 },
    ],
},

React, die Animationen und die Icons zu trennen erlaubt dem Browser, das selten Veränderte zwischenzuspeichern und nur das neu zu laden, was sich ändert. Der Produktions-Build erzeugt zusätzlich ein eigenes SSR-Bundle über vite build --ssr.

Die Stateless-Anforderung

Ein Server, der nie neu startet, behält alles zwischen den Anfragen im Speicher. Genau das macht ihn schnell, und genau das ist seine Falle. Unter Octane würde ein anfragespezifischer Zustand, in einem Singleton oder einer statischen Eigenschaft gespeichert, zur nächsten Anfrage durchsickern, die vom selben Worker bedient wird. Der gesamte Code der Website befolgt daher diese Regel: Kein Anfragezustand wird über seinen Gültigkeitsbereich hinaus behalten.

Der Zähler der Warteliste zum Beispiel wird verzögert geladen, um das Server-Rendering nicht durch einen Netzwerkaufruf zu blockieren, und sein Ergebnis kommt in einer zweiten Anfrage auf der Client-Seite an:

'waitlist' => [
    'count' => Inertia::defer(fn(): ?int => app(FetchWaitlistCountQuery::class)->run()),
],

So bleibt das SSR augenblicklich, und der nicht deterministische Wert tritt nie in das erste Rendering ein. Diese Stateless-Strenge durchzieht jedes Modul des Repos.

Was die Geschwindigkeit noch nicht sagt

Octane, FrankenPHP und Inertia ergeben eine schnelle und indexierbare Website. Diese Nüchternheit geht noch weiter: Der nächste Schritt entfernt die Datenbank selbst, und mit ihr jede lokale Speicherung persönlicher Daten.

Um bei der Öffnung der Beta 0.1 benachrichtigt zu werden, tragen Sie sich in die Warteliste ein.

Dieses Projekt wird hier erzählt, Artikel für Artikel. Kommen Sie an Bord: Ihr Feedback zeichnet den weiteren Weg.

Auf die Warteliste