When a page is slow, “the backend is slow” is often the first assumption. Sometimes it is. Other times, time disappeared into DNS resolution, the TLS handshake, a connection that was not reused, repeated queries, or too much JavaScript before the first render.
The complete path matters because users do not experience layers. They experience the total.
Measure before choosing a culprit
Open the browser’s network panel and inspect the main request. Timings are usually divided into stages:
- DNS resolution;
- connection setup;
- TLS negotiation;
- waiting for the first byte;
- response transfer;
- work performed by the browser after the response.
Server-Timing can expose internal metrics in that same view:
Server-Timing: auth;dur=8, db;dur=42, render;dur=17With that information, a 180 ms wait stops being an opaque block. You can see how much belongs to authentication, the database, rendering, and the rest of the path.
Preserve a correlation identifier
Generate or accept a request-id at the edge and carry it through every internal call. Include it in logs and, when possible, return it in the response headers.
const requestId = request.headers['x-request-id'] ?? crypto.randomUUID();
logger.info({
requestId,
route: request.url,
method: request.method,
});If the operation publishes messages or starts a job, propagate the same context. The goal is to follow the action, not merely an HTTP connection.
Look for multiplied work
Many performance problems do not come from one slow operation. They come from a reasonable operation executed too many times.
A listing that runs one query for the items and then one query per item may work with ten records and collapse with a thousand. The pattern appears as a repeated sequence in traces and as an almost linear increase in latency.
Before adding cache:
- count how many queries a route executes;
- inspect the plans for the most expensive queries;
- verify indexes and returned volume;
- remove data the client does not use;
- only then decide what can be cached.
Cache without an invalidation strategy replaces a speed problem with a consistency problem.
Do not end the analysis at the first byte
A fast response can still produce a slow page. The browser must parse HTML, download resources, execute scripts, calculate styles, and paint the result.
On the frontend, track at least:
- LCP, to know when the main visible content appears;
- INP, to measure response to interactions;
- CLS, to identify unexpected layout shifts;
- the size and execution cost of shipped JavaScript.
Rendering essential content on the server, reserving image dimensions, and loading modules on demand usually produce more reliable gains than isolated micro-optimizations.
Build one timeline
Logs tell you what happened. Metrics reveal frequency and trends. Traces connect the steps of an operation. The three signals complement one another.
Start small: a correlation identifier, total duration per route, database time, and result code. Then add spans for external dependencies and queues. A small set of related data is more useful than a huge collection nobody can query during an incident.
Once every step is visible, performance stops being a discussion based on intuition. The team can locate the cost, compare versions, and decide whether the fix belongs in the browser, the network, the server, or the data model.
