Why Laravel applications get slow: 7 causes we find most often

Why Laravel applications get slow: 7 causes we find most often

Laravel is not slow. Applications built on it get slow, and after a few years the reasons are remarkably consistent. When we take over a Laravel system that "feels sluggish", we rarely find one dramatic bottleneck — we find four or five ordinary ones stacked on top of each other, each individually defensible when it was written.

Here is what we actually find, roughly in the order we find it.

1. The page runs a thousand queries it does not need

The most common cause by a wide margin, and the least visible in code review. An n+1 query pattern looks fine in a controller and is invisible on a developer laptop with fifty records.

On one listing page we inherited, a single request ran 1,072 database queries and hydrated 1,806 Eloquent models. After fixing the eager loading it ran 222 queries and loaded 151 models — memory per request halved from 26 MB to 12 MB, and the page went from 1.21 seconds to 425 milliseconds.

Nothing about the page changed for the user. It simply stopped asking the database the same question a thousand times.

2. The indexes were never added

The one we see most often after n+1, and the easiest to miss: a query that was fast on a small table and was never revisited. A WHERE on an unindexed column, a foreign key with no index, or a composite index whose column order does not match how the query actually filters.

It degrades gradually rather than breaking, so nobody attributes the slowdown to it. The tell is a query whose time grows with table size while the code around it has not changed in years. Adding the right index is often a one-line migration and the largest single improvement available.

3. Search is running through the database when it should not be

A LIKE '%term%' across a large table cannot use an index. It works until the catalogue grows, then degrades quietly — slow first, then timeouts.

On a product information system holding 300,000+ SKUs, product searches ran through plain MySQL and had started timing out on simple lookups. Moving search and filtering to a dedicated search engine with real-time indexing brought lookups from seconds to milliseconds and cut overall database load by roughly 40%.

If your slowest page is a search or filter page, check this first — and note that it is usually not fixable with an index.

4. One endpoint can take the whole server down

Slowness and outages are often the same bug at different traffic levels. On one platform, a single API endpoint could exhaust the server's 512 MB memory limit answering one request. Under normal load it was merely slow. Under real load it was an outage.

Worth asking: is anything in your application loading an unbounded result set — a report, an export, an admin listing with no pagination?

5. The cheap pages are not cheap

Performance work concentrates on the important pages, so the unimportant ones quietly rot. On one site the 404 page returned 431 kB of HTML and loaded a 3 MB image, taking up to 4.9 seconds just to say "not found".

Every crawler, every broken link and every mistyped URL paid that cost. These pages rarely appear in analytics, so nobody notices — but Google crawls them.

6. Reporting queries are competing with the application

Dashboards and reports are written once, usually against a small dataset, and then run daily forever against a growing one. On an events platform we maintain, tuning the MySQL reporting queries was what made the operational dashboards usable again for the team relying on them all day.

The application is fast; the reports are eating the database it depends on.

7. The infrastructure was never revisited

The application grew and the setup hosting it did not. We have seen a 15 GB repository holding two separate Laravel projects, CMS leftovers from an unfinished migration, cached views and years of junk files — large enough that cloning it was a challenge, let alone deploying it. Moving media to object storage and clearing runtime artefacts is not glamorous work, but it is what makes every deploy and every cache rebuild faster.

How to tell which one is yours

Roughly in the order that pays off fastest:

  1. Measure one slow page's query count and memory use before changing anything.
  2. Check the slow query log for queries whose cost scales with table size.
  3. If the slowest page is search or filtering, look there before optimising anything else.
  4. Check whether any endpoint can return an unbounded result set.
  5. Look at the pages nobody looks at — 404s, error pages, feeds.
  6. Separate reporting load from application load.

The pattern worth noticing: none of these is a Laravel problem, and none is fixed by a rewrite. Each is fixed by measuring one page properly and then changing what the measurement points at.

Where we fit

We do this as ongoing work rather than as a one-off engagement — see Laravel support and maintenance for how that works, or the travel platform case study for the full before-and-after behind the numbers above.

system risk check

Not sure what state your application is in?

A System Risk Check is a fixed-price technical audit of your application, database and infrastructure, with a written, prioritised report. The report is yours, whatever you decide next.