Journal de bord · Phase 0 · 2/7
La stack d'aubia.dev : Octane, FrankenPHP et Inertia SSR
Une latence divisée par cinq à dix grâce à Octane et FrankenPHP, un SSR Inertia qui sert le HTML complet dès le premier octet : la stack d'aubia.dev, brique par brique.
- laravel
- octane
- frankenphp
- inertia
- vite

aubia.dev sert son HTML complet dès le premier octet et répond avec une latence divisée par cinq à dix par rapport à un PHP-FPM classique. Pour une landing, ce n'est pas du luxe : une vitrine a un seul travail non négociable, être vue. Par les visiteurs, mais d'abord par les moteurs de recherche et par les moteurs de réponse IA. Je vous ouvre la stack, avec le raisonnement derrière chaque brique.
Le rendu serveur comme invariant SEO
Une application React classique renvoie une coquille HTML vide, puis peuple la page en JavaScript. Un navigateur s'en accommode. Un crawler ou un moteur de réponse, lui, doit trouver le contenu directement dans le HTML de la première réponse.
C'est pour cette raison que je me suis interdit le rendu client seul sur cette vitrine. Le SSR y commande tout le reste. Inertia v3 rend les pages React côté serveur, et le HTML complet part dès le premier octet : titre, sous-titre, sections, métadonnées SEO, tout est présent avant qu'une seule ligne de JavaScript ne s'exécute côté client. L'hydratation prend ensuite le relais pour l'interactivité. Cette règle, le contenu doit exister dans le HTML initial, dicte tout le reste du code frontend, jusqu'à l'interdiction d'afficher du contenu via un useEffect.
Octane et FrankenPHP : le serveur qui ne redémarre pas
J'ai installé Octane dès le premier jour du repo, fin avril 2026, avant même d'écrire la moindre ligne de contenu. C'est un choix de socle, pas une optimisation ajoutée après coup. En PHP traditionnel, chaque requête reboote l'application : chargement du framework, résolution du container, puis réponse. Laravel Octane change ce modèle. L'application démarre une fois, puis des workers persistants servent des milliers de requêtes dans le même processus.
Restait à choisir le driver. J'ai regardé RoadRunner et Swoole, les deux autres options d'Octane, avant de trancher pour FrankenPHP : il embarque le serveur Caddy nativement, avec HTTP/3, certificats automatiques et streaming sans couche supplémentaire à opérer, et sa feuille de route est active dans l'écosystème Laravel.
// config/octane.php
'server' => env('OCTANE_SERVER', 'roadrunner'),
# .env : le driver actif sur aubia.dev
OCTANE_SERVER=frankenphp
Sur ce socle, le bootstrap Laravel reste en mémoire et la latence par requête chute d'un facteur cinq à dix par rapport au cold start de PHP-FPM. Un temps de réponse serveur bas et stable compte pour le score de performance, et donc pour le référencement. Mais ce modèle impose une discipline que j'aborde plus bas.
Inertia v3 : React sans API séparée
Le point qui rend cette stack agréable, c'est Inertia. Il fait le pont entre Laravel et React sans que j'aie à construire et versionner une API REST séparée. Les controllers renvoient des pages React avec leurs props, comme ils renverraient des vues Blade. Le SSR s'active dans la configuration, sans serveur Node distinct à maintenir en développement.
// config/inertia.php
'ssr' => [
'enabled' => true,
'url' => 'http://127.0.0.1:13714',
],
Concrètement, Inertia::render() remplace les vues, les routes restent définies côté Laravel, et React ne se soucie que du rendu. Une seule source de vérité pour le routing, aucune duplication de logique entre un back et un front qui s'ignorent.
Vite 8, Rolldown et React 19
Côté build, aubia.dev utilise Vite 8 avec Rolldown, le bundler écrit en Rust intégré nativement. React 19 est compilé avec le React Compiler, qui mémoïse automatiquement les composants sans annotations manuelles. Le découpage du bundle est piloté explicitement pour isoler les grosses dépendances dans leurs propres chunks :
// 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 },
],
},
Séparer React, les animations et les icônes permet au navigateur de mettre en cache ce qui bouge peu et de ne recharger que ce qui change. Le build de production génère en plus un bundle SSR dédié via vite build --ssr.
La contrainte stateless
Un serveur qui ne redémarre jamais garde tout en mémoire entre les requêtes. C'est justement ce qui le rend rapide, et c'est aussi son piège. Sous Octane, stocker un état propre à une requête dans un singleton ou une propriété statique le ferait fuiter vers la requête suivante, servie par le même worker. Tout le code de la vitrine respecte donc cette règle : aucun état de requête n'est retenu hors de sa portée.
Le compteur de la liste d'attente, par exemple, est chargé de façon différée pour ne pas bloquer le rendu serveur sur un appel réseau, et son résultat arrive en seconde requête côté client :
'waitlist' => [
'count' => Inertia::defer(fn(): ?int => app(FetchWaitlistCountQuery::class)->run()),
],
Le SSR reste ainsi instantané, et la donnée non déterministe n'entre jamais dans le premier render. Cette rigueur stateless irrigue chaque module du repo.
Ce que la vitesse ne dit pas encore
Octane, FrankenPHP et Inertia donnent une vitrine rapide et indexable. Cette sobriété va plus loin encore : la prochaine étape retire la base de données elle-même, et avec elle tout stockage local de données personnelles.
Pour être prévenu à l'ouverture de la beta 0.1, rejoignez la liste d'attente.
Ce chantier se raconte ici, article après article. Embarquez dans l'aventure : vos retours dessineront la suite.
Rejoindre la liste d'attente