Unravel guide

Why is my Laravel app slow? A debugging checklist

When a Laravel app feels slow, the cause is almost always one of a handful of things, and the fastest way to know which is to look at a single slow request end to end rather than at an average on a chart. This checklist goes from the most common cause to the least, with how to confirm each.

1. N+1 queries

The usual culprit. One page quietly fires dozens of near-identical queries because a relation is lazy-loaded inside a loop. Confirm it by looking at a slow request's queries: if you see the same query shape repeated with only an id changing, that's an N+1. Fix it by eager-loading with with(). See the dedicated guide: Debug N+1 queries.

2. Slow individual queries

A single query against an unindexed column, or a heavy join, can dominate a request on its own. Confirm it by sorting a request's queries by duration. The fix is usually an index, a narrower SELECT, or moving the work out of the request path.

3. Work that should be queued

Sending email, calling a third-party API, or generating a PDF inside the request makes the user wait on it. Confirm it by looking for a long outbound HTTP call or mail send in the request timeline. The fix is to dispatch it to a queued job and return immediately.

4. Slow or failing external calls

An outbound HTTP call to a payment gateway or API with no timeout will hang your request as long as the remote service is slow. Confirm it by checking the duration of outbound calls in the trace. Always set a timeout, and queue the call if the response isn't needed to render the page.

5. Cache misses and missing cache

Recomputing the same expensive result on every request, or a cache that isn't being hit, shows up as repeated heavy queries that never needed to run twice. Confirm it by checking whether cache reads are hits or misses around the slow work.

Look at one real slow request

Averages hide the request that's actually slow. The reliable move is to open a single slow trace and read it top to bottom: the queries it ran (and whether any repeat), the jobs it dispatched, the outbound calls it made, and where the time actually went.

That's what Unravel is built for. It captures real production requests as causal traces, sorts them by duration, normalizes queries into fingerprints, and collapses N+1 clusters automatically, so the slowest request tells you exactly why it was slow, on the line of code that caused it. Trace volume is never metered, so you can leave it on and catch the slow requests that only happen under real traffic.

Debug it in production.

Connect your real app in two minutes. Free forever on a 3-day window: the full debugger, not a demo.

Start free