Laravel makes it easy to build modern web applications, but as an application grows, performance can become a serious concern. Slow database queries, inefficient Eloquent relationships, excessive API calls, poorly configured caching, and heavy application logic can all affect response times and user experience.
With Laravel 13, developers have access to the same powerful ecosystem of tools and optimization techniques that make Laravel suitable for everything from small applications to large, high-traffic platforms.
In this guide, we will explore 15 practical Laravel 13 performance optimization techniques that can help you build faster, more scalable, and more efficient applications.
Optimize Your Eloquent Queries
One of the most common causes of poor Laravel performance is inefficient database queries.
Consider this example:
$users = User::all();
foreach ($users as $user) {
echo $user->posts->count();
}
This can generate a large number of database queries because the posts relationship is loaded separately for each user.
Instead, use eager loading:
$users = User::with('posts')->get();
foreach ($users as $user) {
echo $user->posts->count();
}
This significantly reduces the number of queries and helps prevent the well-known N+1 query problem.
You should regularly inspect Eloquent queries when optimizing Laravel applications.
Use Eager Loading Carefully
Eager loading is powerful, but loading everything is not always a good idea.
For example:
$users = User::with([ 'posts', 'comments', 'roles', 'notifications' ])->get();
If thousands of users are returned, this can consume a significant amount of memory.
Instead, load only the relationships actually required by the page:
$users = User::with('posts')->paginate(20);
You can also constrain relationships:
$users = User::with([ 'posts' => function ($query) {
$query->latest()->limit(5);
}
])->get();
The goal is not simply to reduce the number of queries. It is to retrieve only the data your application actually needs.
Select Only Required Database Columns
Avoid retrieving entire database records when you only need a few fields.
Instead of:
$users = User::all();
use :
$users = User::select('id', 'name', 'email')->get();
This reduces the amount of data transferred from the database and decreases memory consumption.
For relationships, you can also limit selected columns:
$posts = Post::with('author:id,name') ->select('id', 'author_id', 'title') ->get();
This becomes especially important when working with large tables.
Add Proper Database Indexes
Laravel optimization is not only about PHP code. Your database is often the most important part of application performance.
Consider:
User::where('email', $email)->first();
If email is not indexed, the database may need to scan a large portion of the table.
Create an index in your migration:
$table->string('email')->index();
For unique values:
$table->string('email')->unique();
Indexes are particularly useful for columns frequently used in:
- WHERE
- JOIN
- ORDER BY
- GROUP BY
However, don't index every column. Indexes also consume storage and can make inserts and updates more expensive.
Use Database Query Analysis
Before optimizing a query, understand what the database is actually doing.
For MySQL, tools such as EXPLAIN can show how a query is executed.
For example:
EXPLAIN SELECT * FROM posts WHERE user_id = 10 ORDER BY created_at DESC;
This can help identify:
- Missing indexes
- Full table scans
- Inefficient joins
- Poor query plans
- Large numbers of examined rows
Laravel applications can become much faster when database bottlenecks are identified instead of optimized blindly.
Use Pagination for Large Datasets
Never load thousands or millions of records into memory when the user only needs a small page.
Instead of:
$posts = Post::all();
use:
$posts = Post::paginate(20);
For very large datasets or background processing, consider cursor-based pagination
$posts = Post::cursorPaginate(20);
Cursor pagination can be particularly useful when navigating large, ordered datasets.
Use Chunking for Large Data Processing
Sometimes you actually need to process thousands of records.
Don't do this:
$users = User::all();
foreach ($users as $user) {
// Process user
}
Instead
User::chunkById(500, function ($users) {
foreach ($users as $user) {
// Process user
}
});
Only a portion of the records is loaded into memory at a time.
For large applications, this can prevent memory exhaustion and improve the reliability of long-running processes.
Cache Expensive Operations
Caching is one of the most effective ways to improve Laravel application performance.
Suppose your application performs an expensive query
$categories = Category::with('books')
->where('active', true)
->get();
Instead of executing it on every request
$categories = Cache::remember(
'active_categories',
now()->addHour(),
fn () => Category::with('books')
->where('active', true)
->get()
);
Subsequent requests can retrieve the result from the cache rather than querying the database again.
Laravel supports multiple cache backends, including Redis and database-backed caching.
Use Redis for High-Performance Caching
For applications with significant traffic, Redis is often a better choice than database-backed caching.
A typical Laravel setup can use Redis for:
- Application cache
- Queues
- Sessions
- Rate limiting
- Temporary data
For example :
Cache::store('redis')->remember(
'popular_posts',
3600,
fn () => Post::orderByDesc('views')->limit(20)->get()
);
Redis stores data in memory, making frequently accessed cached values extremely fast to retrieve.
Move Expensive Operations to Queues
A request should not perform unnecessary heavy work synchronously.
Imagine a user uploads a document and your application needs to:
- Store the file
- Extract its content
- Generate a thumbnail
- Analyze the content with AI
- Send an email
- Update statistics
Doing everything during the HTTP request can make the page feel extremely slow.
Instead, use Laravel jobs :
ProcessDocument::dispatch($document);
The HTTP request can finish quickly while the queue worker processes the expensive operations in the background.
Queues are particularly useful for:
- Emails
- Notifications
- Image processing
- PDF processing
- AI requests
- Data imports
- External API calls
Use Laravel Horizon to Monitor Queues
If your Laravel application relies heavily on Redis queues, Laravel Horizon provides visibility into your queue infrastructure.
It allows you to monitor things such as:
- Job throughput
- Runtime
- Failed jobs
- Queue workloads
- Worker activity
Monitoring is important because queue performance can become a bottleneck just like database performance.
Optimize Laravel Configuration for Production
Laravel applications should not run with development configuration in production.
Before deploying, make sure production configuration is cached :
php artisan config:cache
php artisan route:cache
php artisan view:cache
You can also clear stale caches when necessary :
php artisan optimize:clear
Then rebuild the required caches.
The important principle is to avoid performing unnecessary configuration and route processing on every request.
Enable PHP OPcache
Laravel performance depends heavily on PHP performance.
OPcache allows PHP to store compiled bytecode in memory instead of recompiling PHP files on every request.
On a production server, OPcache should generally be enabled and properly configured.
This is especially important for applications with significant PHP traffic.
Laravel optimization should therefore be considered at multiple levels:
Browser
↓
Nginx / Apache
↓
PHP-FPM + OPcache
↓
Laravel
↓
Redis / Cache
↓
Database
Optimizing only Laravel code while ignoring PHP-FPM, OPcache, Nginx, or MySQL can leave significant performance improvements on the table.
Reduce Unnecessary Application Work
Not every performance problem requires a complicated optimization technique.
Sometimes the best solution is simply to avoid doing unnecessary work.
For example, instead of :
$posts = Post::all();
$posts = $posts->filter(function ($post) {
return $post->published_at !== null;
});
let the database perform the filtering :
$posts = Post::whereNotNull('published_at')->get();
The database is designed to filter and sort data efficiently.
Whenever possible, perform data filtering, sorting, aggregation, and selection at the database level instead of loading unnecessary records into PHP.
Measure Before and After Optimization
One of the biggest mistakes in performance optimization is optimizing without measurement.
Before changing your code, identify the actual bottleneck.
Useful tools and techniques include:
- Laravel Telescope
- Laravel Pulse
- Laravel Horizon
- Database EXPLAIN
- MySQL slow query logs
- PHP-FPM monitoring
- Server monitoring
- Application performance monitoring tools
For example, if a page takes 2 seconds to load, determine where those 2 seconds are being spent.
It might be :
Database queries 1.4s
External API calls 0.4s
Laravel/PHP 0.1s
Rendering 0.1s
In this situation, optimizing Blade templates would have almost no meaningful impact.
The database should be your priority.
Laravel 13 Performance Optimization Checklist
Before deploying a Laravel 13 application, review the following checklist:
Database
- Use eager loading where appropriate
- Avoid N+1 queries
- Select only required columns
- Add appropriate indexes
- Analyze slow queries
- Use pagination
- Use chunkById() for large processing tasks
Laravel
- Cache expensive queries
- Use queues for long-running operations
- Cache configuration
- Cache routes where appropriate
- Cache compiled views
- Avoid unnecessary application work
Infrastructure
- Enable PHP OPcache
- Configure PHP-FPM correctly
- Use Redis for high-performance caching and queues
- Configure Nginx efficiently
- Monitor CPU and memory usage
Monitoring
- Monitor database performance
- Monitor queues
- Track slow requests
- Monitor server resources
- Measure performance before and after changes
Conclusion
Laravel 13 provides an excellent foundation for building modern web applications, but framework features alone do not guarantee good performance.
The biggest performance improvements usually come from identifying and eliminating bottlenecks across the entire stack.
Start with the database. Fix N+1 queries, add appropriate indexes, retrieve only the data you need, and paginate large datasets. Then introduce caching and Redis for frequently accessed data, move expensive operations to queues, and make sure your production PHP environment is properly configured with OPcache and PHP-FPM.
Most importantly, measure before optimizing.
A fast Laravel application is not created by applying every optimization technique possible. It is created by identifying the actual bottlenecks and applying the right optimization at the right layer.
With these practices, Laravel 13 applications can remain fast, efficient, and scalable as traffic and data volumes grow.