Laravel Performance Optimization Guide
Complete Performance Tuning for Laravel Applications
Transform your Laravel application's performance with this comprehensive optimization guide. Learn proven techniques to reduce response times, optimize database queries, implement effective caching strategies, and monitor performance in production environments.
Time Required: 2-4 hours
Difficulty: Intermediate to Advanced
Prerequisites: Laravel application, server access, basic performance monitoring knowledge
Table of Contents
- Performance Overview
- Application Profiling
- Caching Strategies
- Database Optimization
- Code Optimization
- Server Optimization
- Performance Monitoring
- Troubleshooting
Performance Overview
Laravel performance optimization involves multiple layers working together:
Performance Impact Areas
| Optimization Area | Potential Impact | Difficulty | Time Investment |
|---|---|---|---|
| Application Caching | 50-80% faster | Easy | 30 minutes |
| Database Optimization | 60-90% faster | Medium | 2-4 hours |
| Code Optimization | 20-40% faster | Medium | 1-2 hours |
| Server Configuration | 30-60% faster | Advanced | 1-3 hours |
Performance Metrics to Track
- Response Time: Target < 200ms for most pages
- Database Query Time: Target < 50ms per query
- Memory Usage: Target < 256MB per request
- Cache Hit Ratio: Target > 90%
- Concurrent Users: Test your target load
Application Profiling
Step 1: Install Debugging Tools
# Install Laravel Debugbar (development only)
composer require barryvdh/laravel-debugbar --dev
# Install Laravel Telescope for detailed monitoring
composer require laravel/telescope --dev
php artisan telescope:install
php artisan migrate
# Install Clockwork for performance profiling
composer require itsgoingd/clockwork --dev Step 2: Enable Query Logging
Add to your AppServiceProvider for temporary debugging:
// app/Providers/AppServiceProvider.php
use IlluminateSupportFacadesDB;
use IlluminateSupportFacadesLog;
public function boot()
{
if (app()->environment('local')) {
DB::listen(function ($query) {
if ($query->time > 50) { // Log slow queries
Log::info('Slow Query:', [
'sql' => $query->sql,
'bindings' => $query->bindings,
'time' => $query->time . 'ms'
]);
}
});
}
} Step 3: Performance Profiling Command
Create a custom profiling command:
# Generate command
php artisan make:command ProfilePerformance // app/Console/Commands/ProfilePerformance.php
<?php
namespace AppConsoleCommands;
use IlluminateConsoleCommand;
use IlluminateSupportFacadesDB;
use IlluminateSupportFacadesCache;
class ProfilePerformance extends Command
{
protected $signature = 'app:profile';
protected $description = 'Profile application performance';
public function handle()
{
$this->info('Application Performance Profile');
$this->info('===============================');
// Database connections
$this->checkDatabase();
// Cache performance
$this->checkCache();
// Memory usage
$this->checkMemory();
// Configuration optimization
$this->checkConfiguration();
}
private function checkDatabase()
{
$start = microtime(true);
DB::select('SELECT 1');
$time = (microtime(true) - $start) * 1000;
$this->line("Database connection time: {$time}ms");
// Check for missing indexes (example)
$tables = DB::select('SHOW TABLES');
$this->line("Database tables: " . count($tables));
}
private function checkCache()
{
$start = microtime(true);
Cache::put('performance_test', 'test_value', 60);
$value = Cache::get('performance_test');
Cache::forget('performance_test');
$time = (microtime(true) - $start) * 1000;
$this->line("Cache operation time: {$time}ms");
$this->line("Cache driver: " . config('cache.default'));
}
private function checkMemory()
{
$memory = memory_get_usage(true) / 1024 / 1024;
$peak = memory_get_peak_usage(true) / 1024 / 1024;
$this->line("Current memory usage: {$memory}MB");
$this->line("Peak memory usage: {$peak}MB");
}
private function checkConfiguration()
{
$optimizations = [
'config.cache' => file_exists(base_path('bootstrap/cache/config.php')),
'routes.cache' => file_exists(base_path('bootstrap/cache/routes-v7.php')),
'views.cache' => file_exists(base_path('storage/framework/views')),
'opcache.enabled' => ini_get('opcache.enable'),
];
foreach ($optimizations as $check => $status) {
$status = $status ? '✓' : '✗';
$this->line("{$check}: {$status}");
}
}
} Caching Strategies
Step 1: Configuration Caching
Cache Laravel's configuration files:
# Cache configuration (production only)
php artisan config:cache
# Cache routes (production only)
php artisan route:cache
# Cache views
php artisan view:cache
# Clear all caches when needed
php artisan optimize:clear Step 2: Application-Level Caching
Implement strategic caching in your application:
// Cache expensive database queries
public function getPopularPosts()
{
return Cache::remember('popular_posts', 3600, function () {
return Post::with(['author', 'category'])
->where('published', true)
->orderBy('views', 'desc')
->limit(10)
->get();
});
}
// Cache computed values
public function getUserStats($userId)
{
return Cache::remember("user_stats_{$userId}", 1800, function () use ($userId) {
return [
'posts_count' => Post::where('user_id', $userId)->count(),
'comments_count' => Comment::where('user_id', $userId)->count(),
'total_views' => Post::where('user_id', $userId)->sum('views'),
];
});
}
// Tag-based cache invalidation
Cache::tags(['users', 'posts'])->put("user_{$id}", $user, 3600);
// Invalidate tagged cache
Cache::tags(['users'])->flush(); Step 3: Query Result Caching
Cache database queries effectively:
// Model-level caching
class Post extends Model
{
public function scopePopular($query)
{
return $query->orderBy('views', 'desc');
}
public static function getCachedPopular($limit = 10)
{
return Cache::remember("popular_posts_{$limit}", 3600, function () use ($limit) {
return static::popular()->limit($limit)->get();
});
}
// Cache relationships
public function getCachedComments()
{
return Cache::remember("post_comments_{$this->id}", 1800, function () {
return $this->comments()->with('user')->get();
});
}
} Step 4: HTTP Caching
Implement HTTP caching headers:
// Add to middleware or controller
public function show(Post $post)
{
return response()
->view('posts.show', compact('post'))
->header('Cache-Control', 'public, max-age=3600')
->header('ETag', md5($post->updated_at));
}
// Conditional responses
public function api(Request $request, Post $post)
{
$etag = md5($post->updated_at);
if ($request->header('If-None-Match') === $etag) {
return response('', 304);
}
return response()->json($post)
->header('ETag', $etag)
->header('Cache-Control', 'public, max-age=1800');
} Database Optimization
Step 1: Query Optimization
Optimize your database queries:
// Bad: N+1 Query Problem
$posts = Post::all();
foreach ($posts as $post) {
echo $post->user->name; // Executes N queries
}
// Good: Eager Loading
$posts = Post::with('user')->get();
foreach ($posts as $post) {
echo $post->user->name; // Single query
}
// Better: Selective eager loading
$posts = Post::with('user:id,name')->get();
// Advanced: Constrained eager loading
$posts = Post::with(['comments' => function ($query) {
$query->where('approved', true)->latest()->limit(5);
}])->get(); Step 2: Database Indexing
Create proper database indexes:
# Create migration for indexes
php artisan make:migration add_performance_indexes // Migration file
public function up()
{
Schema::table('posts', function (Blueprint $table) {
// Index frequently queried columns
$table->index('published_at');
$table->index('status');
$table->index(['category_id', 'published_at']);
// Full-text search index
$table->fullText(['title', 'content']);
// Composite index for complex queries
$table->index(['user_id', 'status', 'published_at']);
});
Schema::table('users', function (Blueprint $table) {
$table->index('email');
$table->index('created_at');
});
} Step 3: Query Builder Optimization
Use efficient query patterns:
// Use select() to limit columns
$users = User::select('id', 'name', 'email')->get();
// Use chunk() for large datasets
User::chunk(1000, function ($users) {
foreach ($users as $user) {
// Process user
}
});
// Use exists() instead of count() for existence checks
if (Post::where('user_id', $userId)->exists()) {
// User has posts
}
// Use whereHas() efficiently
$users = User::whereHas('posts', function ($query) {
$query->where('published', true);
})->get();
// Raw queries for complex operations
$results = DB::select('
SELECT u.name, COUNT(p.id) as posts_count
FROM users u
LEFT JOIN posts p ON u.id = p.user_id
GROUP BY u.id
HAVING posts_count > 10
'); Step 4: Database Connection Optimization
Configure database connections properly:
// config/database.php
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
PDO::ATTR_PERSISTENT => true, // Connection pooling
PDO::ATTR_TIMEOUT => 5,
]) : [],
// Connection pooling settings
'pool' => [
'max_connections' => 10,
'min_connections' => 1,
],
], Code Optimization
Step 1: Autoloader Optimization
# Optimize Composer autoloader
composer install --optimize-autoloader --no-dev
# Dump optimized autoloader
composer dump-autoload --optimize Step 2: Memory Optimization
Optimize memory usage patterns:
// Use generators for large datasets
function getUsers()
{
$users = User::cursor(); // Returns a lazy collection
foreach ($users as $user) {
yield $user;
}
}
// Process large collections efficiently
$users = User::select('id', 'email')->lazy();
$users->each(function ($user) {
// Process each user with minimal memory impact
$this->processUser($user);
});
// Unset variables to free memory
$largeArray = range(1, 100000);
// ... use $largeArray
unset($largeArray); Step 3: Request Optimization
Optimize request handling:
// Middleware for response compression
class CompressResponse
{
public function handle($request, Closure $next)
{
$response = $next($request);
if (!$response->headers->has('Content-Encoding')) {
$response->headers->set('Content-Encoding', 'gzip');
$response->setContent(gzencode($response->getContent()));
}
return $response;
}
}
// Optimize file uploads
class FileUploadController extends Controller
{
public function store(Request $request)
{
$request->validate([
'file' => 'required|file|max:10240', // 10MB limit
]);
// Process file in chunks
$file = $request->file('file');
$path = $file->storeAs('uploads',
Str::uuid() . '.' . $file->getClientOriginalExtension(),
'public'
);
// Queue heavy processing
ProcessFileJob::dispatch($path);
return response()->json(['message' => 'File uploaded successfully']);
}
} Server Optimization
Step 1: PHP Configuration
Optimize PHP settings for Laravel:
# php.ini optimizations
memory_limit = 512M
max_execution_time = 60
max_input_vars = 3000
post_max_size = 64M
upload_max_filesize = 64M
# OPcache settings (crucial for performance)
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=12
opcache.max_accelerated_files=4000
opcache.revalidate_freq=60
opcache.fast_shutdown=1
opcache.validate_timestamps=0 # Production only Step 2: Web Server Configuration
Nginx configuration for Laravel:
# /etc/nginx/sites-available/laravel
server {
listen 80;
server_name example.com;
root /var/www/html/public;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
index index.php;
charset utf-8;
# Enable gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript
application/javascript application/json
application/xml+rss application/atom+xml;
# Cache static assets
location ~* .(jpg|jpeg|png|gif|ico|css|js|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
error_page 404 /index.php;
location ~ .php$ {
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
# Performance tuning
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
fastcgi_busy_buffers_size 256k;
}
} Step 3: Redis Configuration
Optimize Redis for Laravel caching:
# /etc/redis/redis.conf
maxmemory 256mb
maxmemory-policy allkeys-lru
save 900 1
save 300 10
save 60 10000
# Performance settings
tcp-keepalive 60
timeout 300 Performance Monitoring
Step 1: Application Performance Monitoring
Set up comprehensive monitoring:
# Install Laravel Pulse for real-time monitoring
composer require laravel/pulse
# Publish and run migrations
php artisan vendor:publish --provider="LaravelPulsePulseServiceProvider"
php artisan migrate
# Configure in .env
PULSE_ENABLED=true
PULSE_INGEST=redis
PULSE_CACHE=redis Step 2: Custom Performance Metrics
Create custom performance tracking:
// app/Services/PerformanceMonitor.php
class PerformanceMonitor
{
public static function track($operation, callable $callback)
{
$start = microtime(true);
$memoryStart = memory_get_usage(true);
try {
$result = $callback();
$duration = (microtime(true) - $start) * 1000;
$memoryUsed = memory_get_usage(true) - $memoryStart;
Log::info('Performance Metric', [
'operation' => $operation,
'duration_ms' => round($duration, 2),
'memory_used_mb' => round($memoryUsed / 1024 / 1024, 2),
'timestamp' => now()->toISOString(),
]);
return $result;
} catch (Exception $e) {
Log::error('Performance tracking failed', [
'operation' => $operation,
'error' => $e->getMessage(),
]);
throw $e;
}
}
}
// Usage in controllers
public function index()
{
return PerformanceMonitor::track('posts.index', function () {
return view('posts.index', [
'posts' => Post::with('author')->paginate(20)
]);
});
} Step 3: Database Query Monitoring
// Add to AppServiceProvider
public function boot()
{
if (app()->environment('production')) {
DB::listen(function ($query) {
if ($query->time > 100) { // Log queries > 100ms
Log::warning('Slow Database Query', [
'sql' => $query->sql,
'bindings' => $query->bindings,
'time' => $query->time . 'ms',
'connection' => $query->connectionName,
]);
}
});
}
} Troubleshooting Performance Issues
Issue 1: Slow Database Queries
Symptoms: High response times, database timeouts
Diagnosis:
# Enable MySQL slow query log
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
# Check Laravel query logs
tail -f storage/logs/laravel.log | grep "ms"
# Profile with Telescope
php artisan telescope:install Solutions:
- Add database indexes for frequently queried columns
- Use eager loading to prevent N+1 queries
- Implement query caching for repeated queries
- Consider database read replicas for high traffic
Issue 2: Memory Exhaustion
Symptoms: "Allowed memory size exhausted" errors
Solutions:
# Increase PHP memory limit
ini_set('memory_limit', '512M');
# Use chunking for large datasets
Model::chunk(1000, function ($records) {
// Process records
});
# Use lazy collections
$collection = Model::lazy(); Issue 3: Cache Invalidation Issues
Symptoms: Stale data, cache misses
Solutions:
# Implement cache tagging
Cache::tags(['users', 'posts'])->put($key, $value);
Cache::tags(['posts'])->flush();
# Use cache versioning
$version = config('app.cache_version', 1);
$key = "posts_{$version}";
# Monitor cache hit rates
$hits = Cache::getRedis()->info()['keyspace_hits'];
$misses = Cache::getRedis()->info()['keyspace_misses'];
$hitRate = $hits / ($hits + $misses) * 100; CloudPloy Performance Hosting
CloudPloy provides optimized Laravel hosting with built-in performance features:
🚀 Pre-optimized Infrastructure
- PHP 8.2+ with OPcache enabled
- Redis caching pre-configured
- Nginx with optimal Laravel configuration
- SSD storage with high IOPS
📊 Advanced Performance Monitoring
- Real-time application performance metrics
- Database query analysis and optimization
- Automatic performance recommendations
- Custom performance alerts
🔧 Automated Optimizations
- Auto-scaling based on traffic
- Dynamic cache management
- Database connection pooling
- CDN integration for static assets
Performance Benchmarks
Typical improvements after optimization:
- Response Time: 70-90% faster page loads
- Database Queries: 60-85% reduction in query time
- Memory Usage: 40-60% reduction in memory consumption
- Concurrent Users: 3-5x increase in concurrent capacity
- Server Resources: 30-50% reduction in CPU usage
Next Steps
After optimizing Laravel performance:
- Implement Security Hardening
- Advanced Database Optimization
- Set up Advanced Monitoring
- Configure CDN Integration
Professional Performance Support
Need expert help with Laravel performance optimization?
- 💬 24/7 Performance Experts: Available in your dashboard
- 🔧 Performance Audit Service: We'll analyze and optimize
- 📈 Custom Optimization Plans: Tailored to your application
- 🛡️ Continuous Monitoring: Ongoing performance management
Get High-Performance Laravel Hosting
Experience blazing-fast Laravel applications with CloudPloy:
- 🚀 Pre-optimized PHP 8.2+ with OPcache
- 📊 Real-time performance monitoring
- 🔄 Auto-scaling infrastructure
- 💬 24/7 Laravel performance experts
- 🎁 Free performance audit and optimization
The Free plan covers one server and one app. Compute is billed separately at the provider rate.
Last updated: 2025-08-30