A slow Laravel app costs you users, rankings, and money. The good news is that most performance problems come from the same handful of causes — uncached bootstrapping, blocking operations in the request cycle, and unoptimised queries. This guide covers the fixes I apply to every production Laravel project.
1. Caching Strategies
Config, route, and view caching
These three Artisan commands are the easiest wins and should be part of every production deployment script. They compile your config files, route definitions, and Blade templates into single cached files so Laravel doesn't re-parse them on every request:
Run these after every deployment. If you change a config or route file locally, clear the cache with php artisan optimize:clear before testing.
Redis for data caching
Redis is the right choice for application-level caching in production — it's in-memory, extremely fast, and supports advanced features like cache tags. Set it as your cache driver and use Cache::remember() for expensive operations:
2. Queue Optimization
Move blocking work off the request cycle
Any task that doesn't need to complete before you return a response should be a queued job — emails, notifications, report generation, image processing, third-party API calls. This is the single biggest improvement for perceived performance.
In production, use Supervisor to keep queue workers running and automatically restart them if they crash.
Laravel Horizon for monitoring
Horizon gives you a real-time dashboard for queue throughput, failed jobs, and worker status. It's essential once you're running queues in production:
Access the dashboard at /horizon. Make sure to protect it with the gate in HorizonServiceProvider so it's not publicly accessible.
3. Scaling for High Traffic
Horizontal scaling
For high-traffic apps, you need to scale out rather than up. Run multiple Laravel app servers behind a load balancer (Nginx or AWS ELB). The key requirement is that your app must be stateless — no session or cache data stored on the local filesystem. Use Redis for both:
Database optimisation
Add indexes to every column used in WHERE, ORDER BY, and JOIN clauses. Fix N+1 queries with eager loading. For read-heavy applications, set up a read replica and point Laravel's read connection at it — this splits query load across two servers with almost no code changes.
CDN for static assets
Serve images, CSS, and JS through a CDN like Cloudflare or AWS CloudFront. This reduces load on your origin server and dramatically improves load times for users far from your server location. Set your ASSET_URL in .env to point at your CDN.
4. Additional Best Practices
Runtime optimisations
Enable PHP OPcache on your server — it caches compiled PHP bytecode and is one of the highest-impact single changes you can make. Always run the latest stable PHP version. Use composer install --no-dev --optimize-autoloader in production to minimise the autoloader overhead.
Common mistakes to avoid
- Leaving
APP_DEBUG=truein production — exposes stack traces and kills performance - Using file-based sessions or cache in a multi-server setup — requests hit different servers and lose context
- Not running
config:cacheandroute:cacheafter deployment - Ignoring failed queue jobs — they pile up silently and can indicate bigger problems
- Skipping query profiling — use Laravel Debugbar or Telescope in development to catch slow queries early
Conclusion
Start with the quick wins — config/route/view caching and moving blocking tasks to queues. Then add Redis for data caching and fix your N+1 queries. By the time you need horizontal scaling, you'll have already eliminated most of the performance bottlenecks that would limit you anyway.
For more on database performance specifically, see the MySQL Query Optimization guide. For deployment infrastructure, see Deploying Laravel with Docker and Kubernetes.