Laravel Framework

Performance Optimization & Best Practices

18 min Lesson 40 of 45

Performance Optimization & Best Practices

Optimizing Laravel applications for production requires understanding various caching mechanisms, query optimization techniques, asset management strategies, and monitoring tools. In this comprehensive lesson, we'll explore proven techniques to make your Laravel applications blazingly fast and efficient.

Configuration Caching

Laravel loads configuration files on every request. Cache them for production to eliminate file I/O overhead:

// Cache all configuration files into a single file php artisan config:cache // Clear configuration cache (during development) php artisan config:clear // IMPORTANT: After caching, env() calls return null // Always use config() in your code, never env() outside config files // ❌ BAD - Will break after config:cache $apiKey = env('API_KEY'); // ✅ GOOD - Works with cached config $apiKey = config('services.api.key'); // config/services.php return [ 'api' => [ 'key' => env('API_KEY'), 'secret' => env('API_SECRET'), ], ]; // Deployment script php artisan config:cache php artisan route:cache php artisan view:cache php artisan event:cache
Critical: Never use env() in your application code after running config:cache. All environment variables must be accessed through config() helpers. Direct env() calls will return null when configuration is cached.

Route Caching

Route registration can be slow for large applications. Cache routes for instant loading:

// Cache all routes php artisan route:cache // Clear route cache php artisan route:clear // Limitations with route caching: // ❌ Closures in routes are NOT supported with route caching Route::get('/user', function () { return 'Hello'; }); // This will break route:cache // ✅ Use controller methods instead Route::get('/user', [UserController::class, 'index']); // ✅ Or use invokable controllers Route::get('/user', UserController::class); // Check if routes are cached if (app()->routesAreCached()) { // Routes are cached } // Combine with configuration caching in deployment php artisan optimize // This runs: config:cache, route:cache, view:cache

View Caching

Compile all Blade templates in advance to speed up rendering:

// Compile and cache all views php artisan view:cache // Clear view cache php artisan view:clear // Views are automatically compiled on first access // Caching pre-compiles ALL views for zero compilation time // Check compiled view location storage/framework/views/ // Custom view caching for specific views public function cacheImportantViews() { $views = ['home', 'dashboard', 'products.index']; foreach ($views as $view) { view($view)->render(); } }

OPcache Configuration

OPcache stores compiled PHP bytecode in memory, drastically improving performance:

// php.ini configuration for production opcache.enable=1 opcache.memory_consumption=256 opcache.interned_strings_buffer=16 opcache.max_accelerated_files=20000 opcache.validate_timestamps=0 // Don't check file changes (production only!) opcache.save_comments=1 opcache.fast_shutdown=1 // For development, allow timestamp validation opcache.validate_timestamps=1 opcache.revalidate_freq=2 // After deployment, reload OPcache php artisan opcache:clear // If using Laravel OPcache package // Or manually opcache_reset(); // Check OPcache status <?php phpinfo(); // Look for OPcache section ?> // Monitor OPcache $status = opcache_get_status(); echo "Hits: " . $status['opcache_statistics']['hits']; echo "Misses: " . $status['opcache_statistics']['misses']; echo "Memory: " . $status['memory_usage']['used_memory'];
OPcache Tips:
  • Set validate_timestamps=0 in production for maximum performance
  • Always clear OPcache after deployments
  • Set max_accelerated_files higher than your total PHP files
  • Monitor cache hit ratio (should be >95%)

Database Query Optimization

Optimize database queries to eliminate N+1 problems and reduce query count:

// ❌ BAD - N+1 Query Problem $posts = Post::all(); foreach ($posts as $post) { echo $post->user->name; // Executes query for each post } // Result: 1 + N queries // ✅ GOOD - Eager Loading $posts = Post::with('user')->get(); foreach ($posts as $post) { echo $post->user->name; } // Result: 2 queries only // Multiple relationships $posts = Post::with(['user', 'comments', 'tags'])->get(); // Nested relationships $posts = Post::with('comments.user')->get(); // Conditional eager loading $posts = Post::with([ 'comments' => function ($query) { $query->where('approved', true) ->orderBy('created_at', 'desc') ->limit(5); } ])->get(); // Lazy eager loading (when you forgot to eager load) $posts = Post::all(); $posts->load('user'); // Load relationship after fetching // Select only needed columns $users = User::select('id', 'name', 'email')->get(); // Use indexes on frequently queried columns Schema::table('posts', function (Blueprint $table) { $table->index('user_id'); $table->index('status'); $table->index(['user_id', 'created_at']); // Composite index }); // Chunk large result sets User::chunk(1000, function ($users) { foreach ($users as $user) { // Process user } }); // Use cursor for memory efficiency foreach (User::cursor() as $user) { // Only one model in memory at a time } // Raw queries for complex operations $results = DB::select(' SELECT u.name, COUNT(p.id) as post_count FROM users u LEFT JOIN posts p ON u.id = p.user_id GROUP BY u.id HAVING post_count > 10 ');

Redis Caching

Leverage Redis for high-performance caching and session storage:

// Configure Redis (config/database.php) 'redis' => [ 'client' => 'phpredis', // Faster than 'predis' 'default' => [ 'host' => env('REDIS_HOST', '127.0.0.1'), 'port' => env('REDIS_PORT', 6379), 'database' => 0, ], 'cache' => [ 'host' => env('REDIS_HOST', '127.0.0.1'), 'port' => env('REDIS_PORT', 6379), 'database' => 1, ], ], // Use Redis for cache (config/cache.php) 'default' => env('CACHE_DRIVER', 'redis'), // Cache database queries $users = Cache::remember('users:active', 3600, function () { return User::where('active', true)->get(); }); // Cache with tags Cache::tags(['users', 'premium'])->put('premium-users', $users, 3600); $premiumUsers = Cache::tags(['users', 'premium'])->get('premium-users'); // Invalidate tagged cache Cache::tags(['users'])->flush(); // Cache forever (until manually deleted) Cache::forever('settings', $settings); // Atomic operations Cache::increment('page_views'); Cache::decrement('remaining_slots'); // Lock mechanism for preventing race conditions $lock = Cache::lock('process-order:' . $orderId, 10); if ($lock->get()) { // Process order // Lock will be released automatically after 10 seconds $lock->release(); } // Use Redis for sessions (config/session.php) 'driver' => env('SESSION_DRIVER', 'redis'),

Asset Optimization

Optimize CSS, JavaScript, and images for faster page loads:

// Install Laravel Mix or Vite for asset compilation npm install // Minify assets in production (vite.config.js) export default defineConfig({ build: { minify: 'terser', cssMinify: true, rollupOptions: { output: { manualChunks: { vendor: ['vue', 'axios'], }, }, }, }, }); // Build for production npm run build // Image optimization composer require spatie/laravel-image-optimizer // config/image-optimizer.php 'optimizers' => [ Jpegoptim::class => [ '--strip-all', '--all-progressive', '--max=85', ], Pngquant::class => [ '--force', '--quality=85-100', ], ], // Optimize images on upload use Spatie\ImageOptimizer\OptimizerChainFactory; public function store(Request $request) { $path = $request->file('image')->store('images'); $optimizerChain = OptimizerChainFactory::create(); $optimizerChain->optimize(storage_path('app/' . $path)); } // Lazy load images in views <img src="{{ $image }}" loading="lazy" alt="Description"> // Use CDN for static assets // config/filesystems.php 'cdn' => [ 'driver' => 's3', 'url' => env('CDN_URL'), ], // Versioning for cache busting <link rel="stylesheet" href="{{ mix('css/app.css') }}"> <script src="{{ mix('js/app.js') }}"></script> // Or with Vite @vite(['resources/css/app.css', 'resources/js/app.js'])

Response Compression

Enable gzip compression to reduce bandwidth usage:

// Apache (.htaccess) <IfModule mod_deflate.c> AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css AddOutputFilterByType DEFLATE application/javascript application/json </IfModule> // Nginx (nginx.conf) gzip on; gzip_vary on; gzip_min_length 1024; gzip_types text/plain text/css application/json application/javascript text/xml application/xml; // Laravel middleware for response compression namespace App\Http\Middleware; class CompressResponse { public function handle($request, $next) { $response = $next($request); if (!$request->wantsJson()) { return $response; } if (!str_contains($request->header('Accept-Encoding'), 'gzip')) { return $response; } $content = $response->getContent(); $compressed = gzencode($content, 9); return $response ->setContent($compressed) ->header('Content-Encoding', 'gzip') ->header('Content-Length', strlen($compressed)); } }

Monitoring and Profiling

Monitor application performance and identify bottlenecks:

// Laravel Telescope for development composer require laravel/telescope --dev php artisan telescope:install php artisan migrate // Access at /telescope // Monitor: Requests, Commands, Queries, Jobs, Mail, Notifications, Cache // Laravel Debugbar for development composer require barryvdh/laravel-debugbar --dev // Production monitoring with Laravel Pulse composer require laravel/pulse php artisan vendor:publish --provider="Laravel\Pulse\PulseServiceProvider" php artisan migrate // config/pulse.php 'ingest' => [ 'requests' => true, 'slow_queries' => true, 'exceptions' => true, 'queues' => true, ], // Custom performance logging use Illuminate\Support\Facades\Log; $startTime = microtime(true); // Your code here $executionTime = microtime(true) - $startTime; if ($executionTime > 1) { Log::warning('Slow operation detected', [ 'execution_time' => $executionTime, 'action' => 'ProcessUserData', ]); } // Query logging in development DB::listen(function ($query) { if ($query->time > 100) { // Log queries slower than 100ms Log::warning('Slow query', [ 'sql' => $query->sql, 'bindings' => $query->bindings, 'time' => $query->time, ]); } }); // APM (Application Performance Monitoring) // Use services like: New Relic, Datadog, Scout APM composer require scoutapp/scout-apm-laravel

Production Best Practices Checklist

// 1. Environment Configuration APP_ENV=production APP_DEBUG=false APP_URL=https://yourdomain.com // 2. Cache Everything php artisan optimize php artisan view:cache php artisan event:cache // 3. Use Queue Workers QUEUE_CONNECTION=redis php artisan queue:work --daemon // 4. Enable OPcache // Edit php.ini as shown earlier // 5. Use Redis for Cache and Sessions CACHE_DRIVER=redis SESSION_DRIVER=redis // 6. Optimize Composer Autoloader composer install --optimize-autoloader --no-dev // 7. Set Proper File Permissions sudo chown -R www-data:www-data storage bootstrap/cache sudo chmod -R 775 storage bootstrap/cache // 8. Use HTTPS // Configure SSL certificate in web server // 9. Set Up Monitoring // Install Laravel Pulse or external APM // 10. Regular Maintenance php artisan schedule:run php artisan queue:restart php artisan telescope:prune
Production Deployment Checklist:
  • ✅ Set APP_ENV=production and APP_DEBUG=false
  • ✅ Run php artisan optimize
  • ✅ Enable OPcache with validate_timestamps=0
  • ✅ Use Redis for cache and sessions
  • ✅ Run queue workers as background processes
  • ✅ Optimize Composer autoloader
  • ✅ Enable gzip compression
  • ✅ Use CDN for static assets
  • ✅ Set up monitoring and logging
  • ✅ Configure automated backups

Performance Testing

// Load testing with Apache Bench ab -n 1000 -c 100 https://yourdomain.com/ // Load testing with wrk wrk -t12 -c400 -d30s https://yourdomain.com/ // Laravel Dusk for browser testing php artisan dusk // Measure specific operations use Illuminate\Support\Benchmark; Benchmark::dd([ 'Scenario 1' => fn () => User::all(), 'Scenario 2' => fn () => User::cursor()->count(), ]); // Profile memory usage $memory = memory_get_peak_usage(true) / 1024 / 1024; echo "Peak memory: {$memory} MB"; // Database query count DB::connection()->enableQueryLog(); // Your code $queries = DB::getQueryLog(); echo "Total queries: " . count($queries);

Exercise 1: Optimize a Slow Application

Given a Laravel application with performance issues:

  1. Install Laravel Telescope and identify slow queries
  2. Fix N+1 query problems with eager loading
  3. Implement Redis caching for frequently accessed data
  4. Add database indexes for common queries
  5. Cache configuration, routes, and views
  6. Measure improvement with before/after benchmarks

Exercise 2: Production Deployment Script

Create an automated deployment script:

  1. Pull latest code from git
  2. Run composer install --optimize-autoloader --no-dev
  3. Run migrations
  4. Clear and rebuild all caches
  5. Restart queue workers
  6. Clear OPcache
  7. Run smoke tests
  8. Rollback on failure

Exercise 3: Implement Monitoring Dashboard

Build a custom performance monitoring system:

  1. Track average response time per endpoint
  2. Monitor database query count and duration
  3. Log slow operations (>100ms)
  4. Track cache hit/miss ratio
  5. Monitor queue job processing time
  6. Create admin dashboard showing metrics
  7. Set up alerts for performance degradation

Summary

In this comprehensive lesson, you learned essential performance optimization techniques for Laravel applications. You explored configuration, route, and view caching, OPcache setup, database query optimization, Redis caching, asset optimization, response compression, and monitoring strategies. You also learned production best practices and deployment checklists. These techniques, when applied correctly, can improve your application's performance by 10-100x, resulting in faster page loads, lower server costs, and happier users.

Congratulations on completing the Laravel Framework tutorial! You've mastered Laravel from fundamentals to advanced topics including routing, controllers, models, authentication, authorization, queues, feature flags, and performance optimization. You're now equipped to build professional, scalable, and performant web applications with Laravel.