Optimize Node.js Outbound Requests with Keep-Alive

Making frequent outbound API calls in Node.js can lead to network latency and system resource exhaustion if a new TCP connection is opened for every request. By leveraging HTTP keep-alive, Node.js allows applications to reuse existing TCP connections for multiple outbound HTTP/HTTPS requests, drastically reducing overhead. This article explains how Node.js manages connection reuse using its internal agent architecture, how to enable keep-alive, and the performance benefits it brings to high-throughput systems.

The Problem with Default Behavior

By default, the standard Node.js HTTP/HTTPS agents destroy TCP connections immediately after a response is completed. For every subsequent outbound API request, Node.js must perform a new TCP three-way handshake and, for secure endpoints, an expensive TLS handshake. This process adds significant latency to each round-trip time (RTT) and can lead to ephemeral port exhaustion on the host server under heavy traffic.

How Node.js Leverages Keep-Alive

Node.js manages outbound HTTP requests through the http.Agent and https.Agent classes. When HTTP keep-alive is enabled, the agent keeps the underlying TCP sockets open in a pool after a request finishes, rather than closing them.

When a new request is initiated to the same destination (host and port), the agent retrieves an idle socket from the pool instead of creating a new one. This bypasses the TCP connection setup and TLS handshake entirely, allowing the payload to be sent immediately over the pre-existing socket.

Configuring Keep-Alive in Node.js

To enable connection reuse, you must instantiate a custom agent with the keepAlive option set to true and pass it to your outbound request configuration.

Here is how to configure it using the native https module:

const https = require('https');

// Create a persistent agent
const keepAliveAgent = new https.Agent({
  keepAlive: true,
  maxSockets: 100,      // Maximum active sockets per origin
  maxFreeSockets: 10,   // Maximum idle sockets to keep open in the pool
  keepAliveMsecs: 1000  // Delay before sending keep-alive probes on idle socket
});

const options = {
  hostname: 'api.example.com',
  port: 443,
  path: '/data',
  method: 'GET',
  agent: keepAliveAgent // Instructs Node.js to use the persistent agent
};

const req = https.request(options, (res) => {
  res.on('data', () => {}); // Consume response to free up the socket
});
req.end();

For popular third-party HTTP libraries like Axios, you can pass the custom agent directly into the instance configuration:

const axios = require('axios');
const https = require('https');

const axiosInstance = axios.create({
  httpsAgent: new https.Agent({ keepAlive: true })
});

Key Performance Benefits