Last year we inherited a PHP/Laravel application that was loading in 4.2 seconds. After 3 weeks of systematic optimization work, we brought it to 1.1 seconds. Here are the 10 changes that made the biggest difference, in order of impact.
OPcache stores precompiled script bytecode in memory, eliminating the need to recompile PHP code on every request. Surprisingly, many production deployments we inherit have it either disabled or misconfigured.
; php.ini optimized OPcache settings
opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0 ; disable in production
opcache.save_comments=0Impact on our project: ~35% reduction in TTFB alone.
N+1 queries are the most common performance killer in Laravel applications. We found 23 N+1 issues using Laravel Debugbar. Fixing them with proper eager loading cut database query count from 847 to 34 on the dashboard page.
// Before: N+1 problem
$orders = Order::all();
foreach ($orders as $order) {
echo $order->customer->name; // new query per iteration!
}
// After: eager loading
$orders = Order::with(['customer', 'items.product'])->get();Identify queries that run frequently but whose results don't change often. Cache them in Redis with an appropriate TTL. We cached 12 "hot" queries — the analytics aggregations, product catalog, and permission checks — and saw immediate gains.
Run EXPLAIN on your slow queries. We found 8 tables missing indexes on frequently filtered columns. Adding composite indexes on (user_id, created_at) and (status, updated_at) cut several queries from 800ms to under 10ms.
5. PHP 8.3 upgrade — JIT compiler improvements gave us a measurable boost on CPU-intensive operations.
6. Image optimization pipeline — Serving WebP via Cloudflare with responsive srcset cut median page weight from 3.2MB to 890KB.
7. Horizon queues for async work — Emails, PDF generation, and notifications moved off the request lifecycle entirely.
8. HTTP/2 + CDN — CloudFront for static assets with aggressive cache headers.
9. Session driver → Redis — Swapping file-based sessions for Redis eliminated disk I/O bottlenecks.
10. Lazy loading + code splitting on the frontend — Deferred JS loading and component-level code splitting reduced initial bundle from 1.8MB to 340KB.
Run Laravel Telescope or Debugbar on your staging environment and look at query counts per request. If you're seeing more than 30 queries on a single page load, you almost certainly have N+1 issues worth fixing first.