Skip to main content

Why Is My Website Slow? Diagnosing Latency Layer by Layer

DNS, TLS, TTFB, download and front-end weight. A layer-by-layer method for finding where your page's time goes, starting with one curl command.

The CompleteStatus Team 6 min read

Why Is My Website Slow? Diagnosing Latency Layer by Layer

Want this checked continuously?

CompleteStatus grades your headers, SSL and email security 24/7 — free, commercial use allowed.

Monitor your site free

"The site feels slow" is one of the least actionable bug reports there is, and one of the most common. Slow where? Slow resolving your domain, slow shaking hands with TLS, slow generating the page, slow delivering it, or slow rendering three megabytes of JavaScript after delivery? Each is a different problem in a different layer with a different fix, and guessing wrong means a week optimizing images when the real culprit was an N+1 query. Here's a layer-by-layer method, starting with one command that splits the problem into five parts.

The one-liner that does the triage

curl will happily time every phase of a request for you:

curl -o /dev/null -s -w "dns:        %{time_namelookup}s
connect:    %{time_connect}s
tls:        %{time_appconnect}s
ttfb:       %{time_starttransfer}s
total:      %{time_total}s
" https://example.com/

The output might look like:

dns:        0.062s
connect:    0.097s
tls:        0.184s
ttfb:       1.437s
total:      1.561s

These numbers are cumulative (each includes everything before it), so read the gaps:

  • dns: time to resolve the hostname to an IP.
  • connect − dns: the TCP handshake, a pure network round-trip to your server.
  • tls − connect: the TLS handshake on top.
  • ttfb − tls: time to first byte of the response, i.e. your server thinking. This is the application and database signal.
  • total − ttfb: downloading the response body.

In the example above, DNS, TCP and TLS are all healthy, and 1.25 of the 1.56 seconds is TTFB. This is a back-end problem, and no amount of image compression will touch it. Run the command a few times (the first run pays DNS and connection costs that later ones may cache) and you have a solid first diagnosis in under a minute.

Layer 1: DNS resolution

A slow or flaky DNS provider taxes every first visit, before your server is even involved. Healthy resolution from a warm resolver is typically tens of milliseconds; if you're seeing hundreds consistently, look at:

  • A slow authoritative provider. The budget DNS hosting bundled with a cheap registrar is a common offender. Anycast DNS providers answer from a location near the user instead of one server in one country.
  • Too-clever setups. Long CNAME chains each add a lookup; a very low TTL (like 60 seconds) means resolvers can barely cache you, so more users pay the full lookup price.

Layer 2: TCP and TLS handshakes

The connect gap is physics: a round trip between the probe and your server. There's no software fix for distance: if your users are in Europe and the server is in Oregon, that's ~140 ms before a single byte of HTTP, on every new connection. Fixes are placement fixes: host near your users, or put a CDN in front so the handshake happens at an edge near them.

The TLS gap adds one to two more round trips on top. Worth checking here:

  • Keep-alive actually enabled. If connections aren't reused, every request re-pays TCP + TLS. It's on by default in sane configs, and turned off by a surprising number of legacy snippets (keepalive_timeout 0; in nginx is the classic).
  • HTTP/2 enabled, so one connection multiplexes many requests instead of paying handshakes in parallel.
  • Session resumption (and OCSP stapling where your CA still supports it). Small wins that trim repeat-visit handshakes and avoid a side-trip to the CA.

Layer 3: TTFB, the application and database signal

This is the layer where most "the site got slow over time" stories live. TTFB is everything your stack does between receiving the request and emitting the first byte: routing, application code, database queries, template rendering, external API calls. When it's high, the usual suspects, in rough order of likelihood:

  • N+1 queries. The page loads 50 products with one query, then runs one more query per product for images or prices: 51 round-trips where 2 would do. Invisible in development with ten rows; painful in production with real data. Every ORM has an eager-loading fix; the hard part is noticing.
  • Missing indexes. The query that was instant at ten thousand rows table-scans at ten million. If TTFB has drifted up as data grew, this is the first place to look; EXPLAIN is your friend.
  • No caching where it's cheap. Recomputing an expensive homepage on every request, refetching config from the database per request, calling a third-party API synchronously in the request path.
  • Cold starts and exhausted worker pools. The request isn't slow, the queueing before it is. Watch for TTFB that's fine at 3 AM and terrible at peak.

Layer 4: Download and front-end weight

If total − ttfb is large, you're shipping too many bytes or shipping them badly: unoptimized images (multi-megabyte hero PNGs that should be compressed and properly sized), missing gzip/brotli compression on text assets, no CDN for static files. Beyond what curl can see there's the front-end layer: render-blocking scripts, heavy JavaScript bundles, fonts loaded from three origins. curl measures the document; the browser's DevTools waterfall and a Lighthouse run measure the experience. If curl says everything is fast but users say the site is slow, the problem lives in the browser, not the server.

Measure from outside, and over time

Two habits turn one-off diagnosis into an early-warning system:

  • Measure from outside your own network. From your office, DNS is cached, the route is short, and your CDN edge is warm, so you're testing the best case. Your users get cold caches, distant geography and mobile networks. Checks from external locations (ideally more than one, since one region's numbers won't represent another's) reflect what users actually experience, and catch the regional degradations you'd never see from your desk.
  • Trend response time continuously. Slowness rarely arrives as an event; it arrives as drift: 300 ms in autumn, 500 by December, 900 by February, each week imperceptibly worse than the last. Nobody notices drift without a baseline. A monitor that records response time on every check draws the trend line for you, and a degradation alert ("sustained response time above threshold") catches the slide long before it becomes the outage where the database finally tips over.

From "feels slow" to a graph and a culprit

The layer-by-layer method turns a vague complaint into a specific, fixable finding, and continuous measurement means the next regression announces itself instead of accumulating. CompleteStatus records response time on every check and charts the trend over time, so you can see when the drift started (often suspiciously close to a deploy) and alert on degradation long before hard downtime. And because a fast response isn't automatically a correct one, the same monitors support content assertions too; we covered that failure class in The page loads, but it's broken.

Create a free account, point a monitor at your slowest page, and let the baseline start building. Next time someone says "the site feels slow," you'll have a graph to look at.

Written with AI assistance and reviewed by the CompleteStatus team.