Laravel-performance optimaliseren: de checklist voor 2026
Laatst bijgewerkt: 12-11-2026
Leestijd: 10 min
Een trage Laravel-applicatie is zelden traag om één enkele reden. Meestal is het een combinatie van kleine, oplosbare problemen: een ontbrekende index hier, een N+1-query daar, een cachedriver die nooit is gewijzigd ten opzichte van de standaardwaarde. Elk afzonderlijk item hieronder bespaart misschien een paar milliseconden. Samen maken ze vaak het verschil tussen een applicatie die direct aanvoelt en een applicatie die traag aanvoelt bij echt verkeer.
Deze checklist is georganiseerd per laag: applicatiecode, database, infrastructuur en monitoring. Werk de lijst in volgorde af. Elk item bevat een codevoorbeeld en de concrete impact van de toepassing ervan.
Optimalisaties op applicatieniveau
1. Cache configuratie, routes en views
Laravel leest en parseert bij elk verzoek configuratiebestanden, routedefinities en Blade-templates, tenzij je dat anders aangeeft. In productie zou dit niet vaker dan één keer per deployment moeten gebeuren.
php artisan optimizeDit ene commando cachet in één keer je configuratie, routes, events en gecompileerde views. Voer het uit als onderdeel van je deployment-pipeline, niet handmatig op een live server, en voer het na elke deploy opnieuw uit, aangezien verouderde caches anders achterhaalde configuratie of routes blijven serveren.
php artisan optimize:clearImpact: elimineert de overhead van het parseren van PHP-configuratiebestanden en het opnieuw registreren van honderden routes bij elk afzonderlijk verzoek. Voor applicaties met grote routebestanden of veel service providers kan dit de responstijd merkbaar verkorten, nog voordat je applicatielogica draait.
2. Elimineer N+1-queries met eager loading
Dit is het meest voorkomende prestatieprobleem in Laravel-applicaties, en het is makkelijk te introduceren zonder het te merken. Het lazy loaden van een relatie binnen een lus voert per iteratie één query uit.
// N+1: voert één query per post uit om de auteur te laden
$posts = Post::all();
foreach ($posts as $post) {
echo $post->author->name;
}
// Opgelost: één query voor de posts, één query voor alle gerelateerde auteurs
$posts = Post::with('author')->get();
foreach ($posts as $post) {
echo $post->author->name;
}Vang deze problemen automatisch op tijdens development door lazy loading volledig te blokkeren:
// AppServiceProvider::boot()
Model::preventLazyLoading(! $this->app->isProduction());Impact: een pagina die 50 queries uitvoert door een N+1-probleem, komt met eager loading vaak terug op 2 of 3 queries. Op pagina's die veel van de database gebruiken is dit doorgaans de oplossing met het hoogste rendement van de hele lijst, en dankzij preventLazyLoading() vangt je testsuite regressies op voordat ze productie bereiken.
3. Verplaats niet-essentieel werk naar queues
Alles wat niet klaar hoeft te zijn voordat je de gebruiker antwoordt, zoals het versturen van een e-mail, het genereren van een rapport of het aanroepen van een externe API, hoort niet binnen de request-responsecyclus te draaien.
class SendWelcomeEmail implements ShouldQueue
{
public function handle(User $user): void
{
Mail::to($user)->send(new WelcomeMail($user));
}
}
dispatch(new SendWelcomeEmail($user));Gebruik Queue::route(), toegevoegd in Laravel 13, om vanaf één centrale plek te bepalen waar elke job draait:
Queue::route(SendWelcomeEmail::class, queue: 'emails', connection: 'redis');Impact: verplaatst trage, niet-kritieke taken volledig weg van de request-thread, zodat een gebruiker die een formulier verstuurt direct antwoord krijgt in plaats van te wachten op de API van een e-mailprovider. Draai een queue worker met Laravel Horizon voor inzicht in de doorvoer en fouten van je jobs.
4. Cache dure queries en berekeningen
Als een query of berekening hetzelfde resultaat oplevert voor meerdere gebruikers of verzoeken, bereken het dan één keer en sla het op.
$topProducts = Cache::remember('top-products', now()->addHour(), function () {
return Product::orderBy('sales_count', 'desc')->limit(10)->get();
});Wanneer de vervaltijd van de gecachte waarde alleen verlengd hoeft te worden in plaats van een volledige herberekening, gebruik dan Cache::touch(), toegevoegd in Laravel 13, in plaats van de waarde te lezen en opnieuw weg te schrijven:
Cache::touch('top-products', now()->addHour());Impact: verwijdert herhaalde, identieke databasebelasting voor gegevens die niet bij elk verzoek veranderen, zoals homepage-widgets, navigatiemenu's of dashboardsamenvattingen. Cache::touch() voorkomt specifiek een onnodige lees-en-herschrijf-roundtrip bij het verlengen van de cache.
5. Optimaliseer de autoloader van Composer voor productie
De standaard autoloader van Composer zoekt klassen dynamisch op. In productie zou die zoekactie vooraf berekend moeten zijn.
composer install --no-dev --optimize-autoloaderImpact: genereert een classmap in plaats van tijdens runtime het bestandssysteem te doorzoeken naar klassen, en verwijdert pakketten die alleen voor development bedoeld zijn, zoals test- en debugtools, die overhead toevoegen zonder enige waarde in productie.
Database-optimalisaties
6. Voeg indexen toe aan veelgebruikte kolommen
Elke kolom die wordt gebruikt in een WHERE-, JOIN- of ORDER BY-clausule op een grote tabel heeft een index nodig, en foreign keys moeten altijd een index hebben.
Schema::table('orders', function (Blueprint $table) {
$table->index('user_id');
$table->index(['status', 'created_at']);
});Impact: bij een tabel met tienduizenden rijen of meer dwingt een niet-geïndexeerde zoekopdracht een volledige tabelscan af. Met de juiste index kan een query van seconden naar milliseconden gaan. Gebruik EXPLAIN op je traagste queries om te bevestigen dat indexen daadwerkelijk worden gebruikt voordat je aanneemt dat dit is opgelost.
7. Selecteer alleen de kolommen die je nodig hebt
Het ophalen van elke kolom uit een brede tabel, zeker als die grote tekst- of JSON-kolommen bevat, kost bandbreedte en geheugen, ook als je maar een paar velden nodig hebt.
// Haalt elke kolom op, inclusief kolommen die je niet nodig hebt
$users = User::all();
// Haalt alleen op wat de view daadwerkelijk gebruikt
$users = User::select(['id', 'name', 'email'])->get();Impact: vermindert zowel de gegevens die vanuit de database worden overgedragen als het geheugen dat Laravel besteedt aan het hydrateren van Eloquent-modellen, wat vooral van belang is bij lijstweergaven en API-endpoints die grote collecties teruggeven.
8. Verwerk grote datasets in chunks in plaats van alles tegelijk te laden
All() of get() aanroepen op een tabel met miljoenen rijen probeert elke rij tegelijkertijd in het geheugen te laden.
// Laadt de hele tabel in één keer in het geheugen
User::all()->each(fn ($user) => $user->sendNewsletter());
// Verwerkt 500 rijen per keer
User::chunk(500, function ($users) {
foreach ($users as $user) {
$user->sendNewsletter();
}
});Impact: voorkomt geheugenuitputting bij grote tabellen en houdt het geheugengebruik constant, ongeacht de grootte van de tabel. Dat is essentieel voor elke geplande job, export of bulkupdate die meer dan een paar duizend rijen raakt.
9. Gebruik paginering in plaats van volledige collecties terug te geven
Elk endpoint dat een onbeperkt aantal rijen kan teruggeven, moet worden gepagineerd, zowel voor de prestaties als voor API-consumenten.
return Order::where('status', 'completed')->paginate(25);Impact: begrenst zowel de grootte van het queryresultaat als de JSON-payload die naar de client wordt gestuurd, waardoor responstijden consistent blijven, ongeacht hoeveel gegevens er in de tabel staan.
Infrastructuur en serverconfiguratie
10. Draai PHP 8.3 of nieuwer met OPcache ingeschakeld
Laravel 13 vereist PHP 8.3 als minimum, en die eis is ook een prestatiewinst: moderne PHP bevat JIT-compilatie en algemene engineverbeteringen ten opzichte van oudere versies.
; php.ini
opcache.enable=1
opcache.enable_cli=0
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0Impact: OPcache slaat voorgecompileerde PHP-bytecode op in het geheugen, zodat scripts niet bij elk verzoek opnieuw hoeven te worden geparseerd en gecompileerd. Door validate_timestamps=0 in productie in te stellen, wordt de controle op bestandswijzigingen bij elk verzoek overgeslagen, maar je moet OPcache dan bij elke deploy handmatig legen.
11. Gebruik Redis voor cache, sessies en queues
De standaard file- en databasedrivers voor cache en sessies werken, maar ze zorgen bij elke lees- en schrijfactie voor schijf-I/O of extra databasequeries.
CACHE_STORE=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redisImpact: verplaatst lees- en schrijfacties voor cache, sessies en queues naar het geheugen in plaats van naar schijf of je primaire database. Dit vermindert de belasting van de database die je daadwerkelijke applicatiegegevens bedient en verlaagt de latentie bij elke cachehit.
12. Serveer statische assets via een CDN
JavaScript, CSS, afbeeldingen en lettertypen hoeven niet bij elk verzoek vanaf je applicatieserver te worden geserveerd.
ASSET_URL=https://cdn.yourdomain.comImpact: verplaatst de levering van statische assets naar edge-servers die geografisch dichter bij je gebruikers staan, waardoor zowel de belasting van je applicatieserver als de time-to-first-byte afnemen voor alles wat niet dynamisch wordt gegenereerd.
13. Overweeg Laravel Octane voor applicaties met hoge doorvoer
Standaard PHP-FPM bootst bij elk afzonderlijk verzoek het volledige framework op. Octane, het applicatieserverpakket van Laravel, houdt het framework tussen verzoeken in het geheugen met behulp van Swoole of RoadRunner.
composer require laravel/octane
php artisan octane:install
php artisan octane:start --server=swooleImpact: bij applicaties met veel verkeer verwijdert dit de bootstrapkosten van het framework bij elk verzoek, wat het aantal verzoeken per seconde aanzienlijk kan verhogen. Het is de moeite waard voor API's met hoge doorvoer, maar over het algemeen niet nodig voor typische applicaties met laag tot gemiddeld verkeer, en je moet je code controleren op state die niet tussen verzoeken in mag blijven bestaan.
14. Gebruik de databasedriver van Reverb voor lichtgewicht realtime functies
Als je realtime functies bouwt, zoals live notificaties, dashboards of chat, voegt Laravel 13 een databasedriver toe voor Reverb, de WebSocket-server van het framework, zodat horizontaal schalen niet langer per se Redis vereist.
// config/broadcasting.php
'reverb' => [
'driver' => 'reverb',
// ...
'scaling' => [
'enabled' => true,
'driver' => 'database',
],
],Impact: voor kleinere applicaties en teams die hun infrastructuur slank willen houden, verwijdert dit een Redis-afhankelijkheid die eerder vereist was om WebSocket-verbindingen over meerdere servers te schalen. Applicaties met hoge doorvoer en duizenden gelijktijdige verbindingen moeten nog steeds Redis gebruiken.
Monitoring en continue optimalisatie
15. Installeer Laravel Pulse voor permanente monitoring
Pulse is het eigen, lichtgewicht monitoringdashboard van Laravel, gebouwd om continu in productie te draaien zonder de overhead van een volledige profiler.
composer require laravel/pulse
php artisan vendor:publish --provider="Laravel\Pulse\PulseServiceProvider"
php artisan migrateImpact: geeft je een live overzicht van trage queries, trage endpoints, queue-doorvoer en foutpercentages zonder je code handmatig te hoeven instrumenteren, waardoor het veel makkelijker wordt prestatieregressies op te sporen voordat gebruikers ze melden.
16. Profileer met Laravel Telescope of Blackfire in staging
Pulse is gebouwd voor productiemonitoring in één oogopslag. Gebruik voor diepgaande profilering op verzoekniveau Telescope in staging, of Blackfire voor gedetailleerde call-graphanalyse.
composer require laravel/telescope --dev
php artisan telescope:install
php artisan migrateImpact: toont de exacte queries, cache-aanroepen en job-dispatches achter elk afzonderlijk verzoek, waardoor "deze pagina voelt traag" verandert in een specifieke, oplosbare regel code in plaats van een gok.
Hoe gebruik je deze checklist
Pas niet alle zestien items tegelijk toe. Begin met profileren met Telescope of Blackfire om te zien waar de tijd daadwerkelijk aan wordt besteed. Los N+1-queries en ontbrekende indexen eerst op, omdat die doorgaans het grootste rendement opleveren voor de minste moeite. Ga daarna verder met caching en queues. Behandel infrastructuurwijzigingen zoals Octane en Redis als een tweede ronde, zodra de problemen op applicatieniveau zijn opgelost, omdat ze operationele complexiteit toevoegen die pas de moeite waard is wanneer de makkelijkere winst al is binnengehaald.
Prestatiewerk is zelden een eenmalig project. De meest betrouwbare aanpak is om een monitoringtool zoals Pulse permanent te laten draaien, zodat regressies binnen enkele dagen worden opgemerkt in plaats van tijdens een verkeerspiek te worden ontdekt.