JString Everything Node & Js Related

Understanding & Measuring HTTP Timings with Node.js

Understanding & Measuring HTTP Timings with Node.js

Understanding and measuring HTTP timings helps us to discover performance bottlenecks in client to server or server to server communication. This article explains timings in an HTTP request and shows how to measure them in Node.js.

Before we jump into HTTP timings, let’s take a look at some basic concepts:

Now let’s take a look at the timeline of a usual HTTP Request:

Understanding & Measuring HTTP Timings with Node.js

Timings explained:

How do HTTP timings help to discover bottlenecks?

For example, if your DNS Lookup takes longer time than you expected, the issue might be with your DNS provider or with your DNS caching settings.

When you see longer Time to First Byte durations, you should check out the latency between the endpoints, but you should also check out the current load of the server.

Slow Content Transfer can be caused by inefficient response body like sending back too much data (unused JSON properties, etc.) or by a slow connection as well.

Measuring HTTP timings in Node.js

To measure HTTP timings in Node.js, we need to subscribe to a specific request, response and socket events. Here is a short code snippet how to do this in Node.js, this example focuses only to the timings:

  const timings = {
    startAt: Date.now(),
    dnsLookupAt: undefined,
    tcpConnectionAt: undefined,
    tlsHandshakeAt: undefined,
    firstByteAt: undefined,
    endAt: undefined

  const req = http.request({ ... }, (res) => {
    res.once('readable', () => {
      timings.firstByteAt = Date.now()
    res.on('data', (chunk) => { responseBody += chunk })
    res.on('end', () => {
      timings.endAt = Date.now())
  req.on('socket', (socket) => {
    socket.on('lookup', () => {
      timings.dnsLookupAt = Date.now()
    socket.on('connect', () => {
      timings.tcpConnectionAt = Date.now()
    socket.on('secureConnect', () => {
      timings.tlsHandshakeAt = Date.now()

DNS Lookup only happens with domain names:

// There is no DNS lookup with IP address
const dnsLookup = dnsLookupAt !== undefined ?  
  dnsLookupAt - startAt : dnsLookupAt

TCP Connection happens immediately after the host is resolved:

const tcpConnection = tcpConnectionAt - (dnsLookupAt || startAt)  

TLS handshake (SSL) happens only with https protocol:

// There is no TLS handshake without https    
const tlsHandshake = tlsHandshakeAt !== undefined ?  
      (tlsHandshakeAt - tcpConnectionAt) : undefined

We wait for server to start sending First Byte:

const firstByte = firstByteAt - (tlsHandshakeAt || tcpConnectionAt)  

Content Transfer starts with the first byte:

const contentTransfer = endAt - firstByteAt  

Total Duration is calculated from start and end dates:

const total = endAt - startAt  

Too see the whole example together check out our https://github.com/RisingStack/example-http-timings repository.

Tools to measure timings

Now that we know how to measure HTTP timings with Node, let’s talk about existing tools that you can use to understand your HTTP requests.

request module

The popular request module has a built-in method to measure HTTP timings. You can enable it with the time property.

const request = require('request')

  uri: 'https://risingstack.com',
  method: 'GET',
  time: true
}, (err, resp) => {
  console.log(err || resp.timings)

Distributed tracing

It’s possible to collect HTTP timings with distributed tracing tools and visualize them on a timeline. This way, you can have a full picture of what’s happening in the background and how much is the real cost of building distributed systems.

RisingStack’s opentracing-auto library has a built-in flag to collect all the HTTP timings with OpenTracing.

Understanding & Measuring HTTP Timings with Node.js

HTTP Request timing with opentracing-auto in Jaeger.


Measuring HTTP Timings with Node.js can help to discover performance bottlenecks. The Node ecosystem provides great tools to extract these metrics from your application.