Slow Response Times
Slow is harder to debug than down, because everything still works. The fastest way through it is to stop guessing which layer is slow and measure it — step 2 below splits the request into DNS, TCP, TLS, and application time, and usually ends the investigation on its own.
Symptom
Section titled “Symptom”Response times are above where they normally sit, or a check failed with the
error category timeout. A timeout is recorded as a failed check rather than a
slow one, but it is the same problem past a threshold: the response did not
arrive in time, not that the server refused.
Likely causes
Section titled “Likely causes”- One slow dependency. A database query that lost its index, an N+1 that grew with your data, or a third-party API called inline during the request.
- Cold starts. A serverless function or a container that scaled to zero. Look for slowness that correlates with quiet periods rather than busy ones.
- The handshake, not the application. DownBad measures the whole request from a cold connection, so DNS resolution and TLS negotiation are included. A slow handshake shows up here even when your application is fast.
- Resource exhaustion. CPU saturation, memory pressure and swapping, a full disk, or an exhausted database connection pool.
- A timeout set below your actual p95. The check is right about the number and wrong about the threshold.
- A neighbour on shared hosting, or a noisy neighbour on an oversubscribed VM.
- Distance. A single-region origin serving a global audience is slow for most of that audience, all the time.
Fix steps
Section titled “Fix steps”-
Compare the check’s latency with your own numbers over the same window. If your APM says the application responded in 80ms and the check measured 4 seconds, the time went somewhere outside your application, and step 2 finds it.
-
Split the request into its phases. This is the single most useful command on this page:
Terminal window curl -sS -o /dev/null -w \'dns=%{time_namelookup}s tcp=%{time_connect}s tls=%{time_appconnect}s ttfb=%{time_starttransfer}s total=%{time_total}s\n' \https://example.comThe numbers are cumulative, so subtract to get each phase.
-
ttfbminustlsdominates → it is your application. Start with the slow query log; a missing index is the most common single cause. Then look for third-party calls made during the request. -
tlsminustcpdominates → it is the handshake. Check for an unnecessarily long certificate chain, a missing intermediate that forces the client to fetch it, or OCSP stapling that is disabled or failing. -
dnsdominates → it is resolution. See DNS Records Changed Unexpectedly for how to test the authoritative servers directly. -
tcpminusdnsdominates → it is the network path or the accept queue. Check whether the server is out of file descriptors or workers, and whether a load balancer is queueing. -
Check the machine during a slow window. Load average, memory and swap, disk space, and connection pool saturation. Look for a cron job, a backup, or a log rotation that overlaps.
-
Only then consider the threshold. If everything is genuinely healthy and the timeout is simply tighter than reality, raise it — but set it from your measured p95, not from a number that makes the alert stop.
Prevention
Section titled “Prevention”- Set timeouts from measurement. Measure p95, add headroom, and revisit it after any change that alters the work per request.
- Never call a third-party API inline on a path that must stay fast. If you must, give it a hard timeout well below your own.
- Cache what does not change, and make sure the cache is actually being hit.
- Alert on degradation, not only on failure. A response time that has doubled is information; a timeout is what it becomes later.
- Watch the trend rather than the spike. A single slow check is noise; a fortnight of creeping p95 is a capacity plan.
- If the slowness never reproduces from your own network, check whether the check is being throttled or challenged — see False Positives.