Hasina Razafintsalama

Hasina RAZAFINTSALAMA

← Retour au Blog
Backend

Optimiser les performances d'une application Laravel : le guide pratique

La performance Laravel est une séquence, pas un tour de magie : mesurer, corriger les requêtes, puis recourir au cache, aux queues et à Octane. Voici l'ordre complet avec du code à chaque étape.

2026-07-15·12 min

Le travail de performance Laravel est une séquence, pas un sac d'astuces. On mesure pour trouver le vrai goulot, on corrige l'accès à la base qui cause l'essentiel de la lenteur, puis on superpose cache, queues et enfin Octane. Faire les choses dans cet ordre fait que chaque étape amplifie la précédente au lieu de masquer un problème jamais corrigé.

Mesurer avant d'optimiser

Ne devinez jamais un goulot. Utilisez Telescope en staging pour inspecter en détail requêtes, requests et jobs (gardez-le derrière une authentification, jamais ouvert en production). Utilisez Debugbar ou Clockwork en local pour voir le nombre de requêtes et le timing par requête. En production, le slow query log de la base et une sonde rapide DB::listen vous disent quelles requêtes font vraiment mal.

Requêtes N+1 et eager loading

La cause la plus fréquente d'un endpoint Laravel lent est une boucle qui charge une relation en lazy, transformant une page en des dizaines de requêtes quasi identiques. Corrigez avec with, withCount et loadMissing, et hors production appelez preventLazyLoading pour que tout lazy load lève une exception et que le problème apparaisse dans les tests plutôt qu'en production.

php
// N+1 : une requête pour les commandes, puis une par commande pour le client
$orders = Order::latest()->limit(50)->get();
foreach ($orders as $order) {
    echo $order->customer->name;
}

// Corrigé : deux requêtes au total
$orders = Order::with('customer')->latest()->limit(50)->get();

// app/Providers/AppServiceProvider.php
Model::preventLazyLoading(! app()->isProduction());

Index de base de données

Lancez EXPLAIN sur les requêtes lentes. Indexez les colonnes utilisées dans WHERE, ORDER BY et JOIN. Pour un index composite, ordonnez les colonnes de la plus sélective à la moins sélective, en suivant l'ordre de filtrage de la requête. Une requête qui filtre et trie sur les colonnes couvertes par un index ne touche jamais les lignes de la table.

php
Schema::table('orders', function (Blueprint $table) {
    $table->index(['status', 'created_at']); // filtre par statut, tri par date
});

Hygiène des requêtes sur les gros volumes

Ne sélectionnez que les colonnes que vous utilisez, pour qu'une table large ne transporte pas des mégaoctets que vous jetez. Ne chargez jamais un gros résultat entièrement en mémoire : utilisez lazy ou cursor pour streamer les lignes, chunkById pour les jobs batch, et paginez chaque endpoint de liste.

php
Order::where('status', 'pending')
    ->select(['id', 'customer_id', 'total'])
    ->lazy()
    ->each(fn (Order $order) => $order->reconcile());

Cache ciblé

Mettez en cache les lectures coûteuses répétées bien plus souvent qu'elles ne changent, pas toutes les requêtes. Les tags de cache permettent d'invalider une tranche sans tout vider, ce qui garde les écritures peu coûteuses. Mettez en cache le résultat calculé, pas la réponse HTTP entière, sauf si la page est entièrement cacheable.

php
$topProducts = Cache::tags(['products', 'dashboard'])
    ->remember('top-products', now()->addMinutes(30), function () {
        return Product::withCount('orders')
            ->orderByDesc('orders_count')
            ->limit(10)
            ->get();
    });

// Invalider seulement la tranche products
Cache::tags(['products'])->flush();

Déplacer le travail lourd vers des queues

Les emails, exports, génération de PDF et appels tiers n'ont pas leur place dans le cycle de requête. Dispatchez-les vers une queue et retournez immédiatement. Horizon donne des métriques en temps réel sur le débit, le temps d'attente et les échecs, pour que les jobs cessent de disparaître silencieusement dans failed_jobs. Utilisez le batching de jobs quand une tâche se ramifie en de nombreuses unités de travail.

Cache au niveau HTTP

Ajoutez des headers ETag et Cache-Control aux endpoints GET dont les données changent lentement, pour que clients et proxies servent un 304 au lieu d'une réponse complète. Servez les assets depuis un CDN. Pour les pages identiques pour tous les visiteurs, un cache de réponse complet devant Laravel retire complètement le framework du chemin critique.

Caches au moment du déploiement

À chaque déploiement, lancez config:cache, route:cache, view:cache et event:cache pour que Laravel ne les reconstruise pas à chaque requête. Activez OPcache sur le serveur, et installez avec composer install --optimize-autoloader --no-dev. Ce sont des gains gratuits faciles à oublier.

Octane, en dernier

Le PHP-FPM traditionnel démarre tout le framework à chaque requête. Octane, sur FrankenPHP ou Swoole, garde l'application en mémoire entre les requêtes et retire une part réelle du temps de réponse sous charge. C'est un multiplicateur sur une app déjà rapide, pas un correctif pour une app lente, et il introduit des pièges de fuite d'état : pas d'état statique mutable, gestion prudente des singletons du container.

php
// Dangereux avec Octane : l'état statique persiste entre les requêtes
class ReportCache
{
    private static array $cache = []; // fuite entre requêtes, à éviter
}
ÉtapeEffortGain typique
Profiler les requêtes lentesFaibleIndique où va le reste de l'effort
Corriger les N+1 et ajouter des indexFaible à moyenSouvent la plus grosse amélioration seule
Hygiène des requêtes (select, lazy, paginer)FaibleSupprime les pics mémoire et les timeouts
Cache ciblé avec tagsMoyenFort sur les endpoints à forte lecture
Déporter le travail vers des queuesMoyenRéduit le temps de réponse des endpoints d'écriture
Caches de déploiement et OPcacheFaiblePetit mais gratuit, à chaque requête
Laravel OctaneMoyen à élevéMultiplie une app déjà rapide sous charge

Mesurez avant d'optimiser. Octane ne corrigera pas des requêtes N+1 et le cache ne corrigera pas un index manquant. Profilez d'abord, corrigez la cause racine, puis recourez aux outils qui multiplient une app déjà rapide.

FAQ

Par quoi commencer pour accélérer une app Laravel ?
Mesurez d'abord avec Telescope ou Debugbar pour trouver les requêtes les plus lentes, puis corrigez les requêtes N+1 et les index manquants. Ces deux points expliquent l'essentiel de la lenteur réelle. Le cache et Octane viennent après, et ils multiplient une app déjà rapide plutôt que de sauver une app lente.
Octane vaut-il le coup ?
Oui, une fois le travail de base de données optimisé. Octane garde le framework démarré entre les requêtes et retire une part réelle du temps de réponse sous charge. Sur une app qui fait encore des N+1, il fait juste tourner le code lent plus vite, et les pièges de fuite d'état sont réels, donc c'est la dernière étape, pas la première.
Comment détecter les requêtes N+1 ?
Debugbar ou Telescope montrent le nombre de requêtes par requête HTTP ; un endpoint de liste qui déclenche des dizaines de requêtes quasi identiques en est la signature. Hors production, appelez Model::preventLazyLoading() pour qu'un lazy load lève une exception et que le N+1 apparaisse dans les tests et les runs locaux.
Le cache Laravel ralentit-il les écritures ?
Les lectures en cache sont rapides ; le coût est l'invalidation. Si chaque écriture vide un cache large, vous échangez de la vitesse de lecture contre du churn d'écriture. Les tags de cache permettent d'invalider juste la tranche concernée, ce qui garde les écritures peu coûteuses tout en gardant les lectures chaudes en cache.
FrankenPHP ou Swoole pour Octane ?
Les deux gardent le framework en mémoire. FrankenPHP est plus simple à déployer, un binaire unique avec HTTPS et workers intégrés, et c'est la recommandation par défaut actuelle. Swoole expose plus de primitives comme les coroutines et les tables si vous en avez besoin. Commencez par FrankenPHP.

Suivez l'ordre : mesurer, corriger requêtes et index, assainir l'accès aux données, puis cache, queues et cache HTTP, et seulement ensuite Octane. La plupart des apps Laravel récupèrent l'essentiel de leur vitesse dans les deux premières étapes, et le reste de la liste la garde rapide à mesure que le trafic grandit.

Besoin d'aide sur ce sujet ? Audit Technique

Découvrir ce service