- Measure before you change anything, or you will optimise the wrong thing.
- Repeated database queries and missing indexes cause most slowness.
- Queues and caching move work out of the request, which users feel immediately.
- Server settings such as OPcache and PHP version matter as much as code.
Measure first, then fix
Performance work goes wrong when it starts with guesses. A developer reads that a package is slow, swaps it out, and the page still takes three seconds because the real cost was one query executed two hundred times. Spend the first hour measuring, and the remaining hours become far cheaper.
Useful tools are free or already in the ecosystem. Laravel Telescope or Debugbar locally show queries, timings and memory per request. Laravel Pulse and an error tracker in production show slow routes, slow jobs and slow queries over time. Your database's slow query log and EXPLAIN output tell you whether an index is used. Pick three or four of the slowest pages that matter to the business, such as checkout, search, the dashboard and login, and record their current response times as a baseline.
Each quick win below is ordered by how often it solves the problem, not by how impressive it sounds. Work through them in order and re-measure after each.
The ten quick wins in order
Do these in sequence and measure after each one so you know what actually helped.
- 1. Fix N+1 queries with eager loadingLoad related records with with() instead of querying inside loops. In development, call Model::preventLazyLoading() so any lazy load throws an error and gets noticed.
- 2. Select only the columns you needAvoid loading wide rows when a list needs three fields. Smaller result sets mean less memory and faster responses.
- 3. Add indexes for real queriesIndex columns used in where, join and order by clauses, guided by the slow query log. Do not index everything, since each index slows writes.
- 4. Cache configuration, routes and viewsRun php artisan optimize in deployment so config, routes, events and views are cached rather than rebuilt on each request.
- 5. Cache expensive resultsStore computed dashboards, counts and external API responses in Redis or another cache store with sensible expiry and clear invalidation.
- 6. Move slow work to queuesEmails, PDF generation, imports, webhooks and image processing should run as queued jobs, with retries and a failed-jobs table.
- 7. Paginate and chunkNever load an entire table into memory. Use pagination for screens and chunk or lazy methods for batch work.
- 8. Optimise Composer and autoloadingDeploy with an optimised autoloader and without development packages to cut file lookups and memory.
- 9. Tune PHP and the serverEnable OPcache, run a supported PHP version, size PHP-FPM workers to your memory, and put static files behind a CDN or caching layer.
- 10. Trim front-end weightCompress images, defer scripts, build assets for production and remove unused packages, since many slow pages are slow in the browser.
Why queries cause most of the pain
Eloquent makes data access pleasant, which is exactly why it hides cost. A table listing fifty orders with their customer, items and shipping status can trigger well over a hundred queries without a single obvious line of code to blame. The page works in development with ten rows and crawls in production with real data.
The fix is mostly discipline. Eager load what the view needs, count with withCount instead of looping, and keep an eye on queries that run inside Blade templates. When a report is genuinely heavy, move it to a summary table or a scheduled job instead of calculating it live on every visit.
Database indexes deserve a calm approach. Look at the queries that run most often and cost most in total, check their execution plans, and add indexes that match how the data is filtered and sorted. If you are unsure how an index will behave on a large table, test on a copy of production data first.
Caching without regret
Caching is the quickest win and the easiest to get wrong. Before caching anything, write down three things: what the data is, how stale it may be, and what event should clear it. A product catalogue that changes twice a day can be cached for minutes. A stock level on a flash sale cannot. If you cannot answer the staleness question, the data is not ready to cache.
Use cache tags or clear keys on the model events that change the data, rather than hoping a timer expires at the right moment. Cache the result of expensive work, such as an aggregate or an external API call, not every small query, because the database often answers simple lookups faster than you expect once indexes exist.
Be careful with per-user content. Caching a full page that includes a customer's name or cart total shows one person another person's data. Cache the shared parts and fetch the personal parts separately.
Which fix helps which symptom
Use this to match what you see with where to look first.
Symptom | Likely cause | First thing to try |
|---|---|---|
Slow list or dashboard pages | N+1 queries, missing indexes | Query log, eager loading, indexes |
Slow only under traffic | Not enough workers, no caching | PHP-FPM tuning, Redis cache, queues |
Slow form submissions | Mail, PDFs or APIs inside the request | Move work to queued jobs |
Memory errors on reports or imports | Loading full tables at once | Chunking, lazy collections, batch jobs |
Slow first load, fast afterwards | Cold caches, no OPcache | Run the optimize command, enable OPcache |
Fast server but slow in the browser | Heavy assets and scripts | Image compression, bundling, CDN |
When to go beyond quick wins
If the ten steps are done and the app is still slow, these are the next options, in rough order of effort.
Laravel Octane
Keeps the application booted between requests using Swoole, RoadRunner or FrankenPHP. It can help a lot but requires code that is safe to run in a long-lived process.
Read replicas
Send reporting and read-heavy traffic to a replica database so writes are not blocked.
Full-page and fragment caching
Cache whole responses for pages that are the same for most visitors.
Search engine for search
Use Meilisearch, Algolia or Elasticsearch instead of database LIKE queries once catalogues grow.
Horizon and queue tuning
Separate queues by priority and size workers per queue, so a large import cannot delay password reset emails.
Architecture review
If one module dominates cost, it may need restructuring. See our SaaS architecture overview for where those boundaries sit.
Queues as a user experience feature
Customers do not care that an invoice PDF takes four seconds to render. They care that the button feels instant. Moving that work to a queue lets the request return straight away, then a worker finishes the job and sends the result by email or a notification. The same pattern fixes timeouts on imports, webhooks from payment gateways and image resizing.
Run the worker under a supervisor so it restarts after a crash or deployment, set sensible retry counts and back-off, and watch the failed-jobs table. A queue nobody monitors is a place where orders and emails quietly disappear.
Common mistakes when tuning Laravel
These waste time or make things worse.
- Caching without invalidationA stale price or stock number is a worse bug than a slow page. Decide how cached data refreshes before you add it.
- Running config:cache locallyCached config ignores changes to environment files. Use it in deployment, not in daily development.
- Using env() outside config filesAfter config caching, direct env() calls return nothing. Read values through config() instead.
- Optimising without a baselineWithout before and after numbers, you cannot prove the work helped.
- Adding indexes blindlyToo many indexes slow inserts and updates and take disk space.
- Upgrading hardware firstA bigger server hides a query problem for a while and then costs more every month.
- Staying on old PHP and Laravel versionsOlder versions miss performance and security work. Our Laravel upgrade and migration service handles this safely.
Hosting and speed for Pakistani projects
Where the app runs matters as much as how it is written. If most of your users are in Pakistan, a server in a faraway region adds delay to every request, and a cheap shared plan with limited PHP workers will collapse during a sale or a campaign. Choose a VPS or cloud instance in a region close to your audience, confirm you can install Redis and a queue worker, and keep a staging server that mirrors production.
Local conditions deserve a thought as well. Many shoppers browse on mobile data, so image weight and script size decide whether a page feels fast. Cash on delivery stores also tend to see traffic spikes around promotions, which is exactly when uncached catalogues and synchronous order emails fail. Plan for the spike, not the average day.
If your store runs on WordPress or Shopify instead, the principles are similar, and we cover them in website speed optimization. For custom Laravel applications, our Laravel ecommerce development team builds with these habits from the first sprint.

Junaid's take
In my experience, nine out of ten slow Laravel applications have a database problem hiding behind a server complaint. Teams ask for a bigger machine, when what they need is an afternoon with a query log. I always start there, and I am rarely surprised.
My order of operations is simple. Measure the pages that earn money, fix the queries, add caching where freshness allows it, push anything slow into the queue, and only then discuss infrastructure. Octane and similar tools are worth knowing, but I would not introduce a long-lived process model to an app that still has lazy loading in its main list view.
If you want a second pair of eyes, our Laravel development services include performance reviews, and you can contact us with your slowest page and we will tell you what we would check first. You may also like our comparison of Livewire, Inertia and API-based front ends, since the choice affects speed too.

