Laravel Speed Best Practices
Laravel is elegant but can be slow without proper optimization. Learn the key performance best practices for production Laravel applications.
BadPageSpeed Team· Performance EngineeringJune 11, 20263 min read
Laravel's developer experience is excellent, but its abstraction layers add overhead. Without optimization, Laravel apps commonly have TTFB of 500ms-2s. Here's how to get your Laravel app responding in under 100ms.
Quick Wins (15 Minutes)
1. Enable Route and Config Caching
# Cache routes (eliminates route parsing on every request)
php artisan route:cache
# Cache config (eliminates .env parsing)
php artisan config:cache
# Cache views (pre-compiles Blade templates)
php artisan view:cache
Impact: -50-200ms TTFB
2. Enable OPcache
; php.ini
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.revalidate_freq=60
opcache.fast_shutdown=1
Impact: -100-300ms TTFB (PHP compilation cached in memory)
3. Use Production Mode
APP_ENV=production
APP_DEBUG=false
Debug mode adds logging, query tracking, and error rendering overhead.
Database Optimization
Eager Loading (Fix N+1)
// BAD: N+1 queries (1 + N queries)
$posts = Post::all();
foreach ($posts as $post) {
echo $post->author->name; // 1 query per post
}
// GOOD: Eager loading (2 queries total)
$posts = Post::with('author')->get();
foreach ($posts as $post) {
echo $post->author->name; // No additional query
}
Select Only Needed Columns
// BAD: Fetches all columns including large text fields
$users = User::all();
// GOOD: Only fetches needed columns
$users = User::select('id', 'name', 'email')->get();
Add Indexes
// Migration
Schema::table('posts', function (Blueprint $table) {
$table->index('user_id');
$table->index(['status', 'published_at']);
$table->index('slug');
});
Use Query Caching
// Cache expensive queries
$popularPosts = Cache::remember('popular-posts', 3600, function () {
return Post::where('views', '>', 1000)
->orderBy('views', 'desc')
->limit(10)
->get();
});
Caching Strategies
Full-Page Caching
// Using spatie/laravel-responsecache
// Automatically caches full HTTP responses
Route::middleware('cacheResponse:3600')->group(function () {
Route::get('/', [HomeController::class, 'index']);
Route::get('/blog', [BlogController::class, 'index']);
});
Fragment Caching
@cache('sidebar-widget', 3600)
<div class="sidebar">
{{-- Expensive sidebar content --}}
@foreach($categories as $category)
<a href="{{ $category->url }}">{{ $category->name }}</a>
@endforeach
</div>
@endcache
Redis as Cache Driver
CACHE_DRIVER=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redis
Queue Heavy Operations
// BAD: Sends email synchronously (blocks response)
Mail::to($user)->send(new WelcomeEmail());
// GOOD: Queues email (response returns immediately)
Mail::to($user)->queue(new WelcomeEmail());
Frontend Asset Optimization
Vite Integration
// vite.config.js
import laravel from 'laravel-vite-plugin';
export default {
plugins: [
laravel({
input: ['resources/css/app.css', 'resources/js/app.js'],
}),
],
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['alpinejs', 'axios'],
},
},
},
},
};
Performance Monitoring
Laravel Telescope (Development)
composer require laravel/telescope --dev
php artisan telescope:install
Laravel Debugbar (Development)
composer require barryvdh/laravel-debugbar --dev
TTFB Results
| Optimization | TTFB |
|---|---|
| Default Laravel | 500-2000ms |
| + Route/Config cache | 200-500ms |
| + OPcache | 100-300ms |
| + Redis cache | 50-150ms |
| + Full-page cache | 10-50ms |
Monitor Your Laravel App
Laravel apps need continuous monitoring as traffic and data grow. BadPageSpeed tracks your frontend performance.
Ready to stop wasting ad spend?
Track your landing page performance, monitor Core Web Vitals, and calculate exactly how much slow pages cost you.
Start Free — No Credit Card