Axios Connection Timeout vs Socket Timeout Guide

Managing network timeouts properly is essential for building resilient applications, preventing hung processes, and handling slow or unresponsive upstream services. In network communication, a connection timeout and a socket (read) timeout represent two distinct phases of an HTTP request. This guide explains the differences between connection timeouts and socket timeouts and demonstrates how to configure both when using Axios in Node.js.

Understanding Connection Timeout vs. Socket Timeout

Before configuring Axios, it is important to distinguish how these two timeouts function:

By default, the standard timeout property in Axios (axios.create({ timeout: 5000 })) applies to the entire request lifecycle—from the initial request dispatch until the response data is fully received. It does not natively separate connection establishment from socket read inactivity.

Configuring Socket Timeout with HTTP/HTTPS Agents

In Node.js, Axios relies on the native http and https modules. You can configure socket-level timeouts by passing custom http.Agent and https.Agent instances to Axios.

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

// Create custom agents with socket inactivity timeouts
const httpAgent = new http.Agent({
  keepAlive: true,
  timeout: 10000 // Socket timeout in milliseconds (10 seconds)
});

const httpsAgent = new https.Agent({
  keepAlive: true,
  timeout: 10000 // Socket timeout in milliseconds (10 seconds)
});

const client = axios.create({
  httpAgent,
  httpsAgent
});

When configured this way, the underlying TCP socket will emit a timeout event if no data is transmitted across the socket for 10 seconds.

Configuring a Dedicated Connection Timeout

Because Node.js and Axios do not provide a dedicated connectionTimeout configuration property out of the box, you can implement one by intercepting the request's socket creation and applying a timer that clears once the socket connects.

Using Request Hooks and Socket Events

You can listen for the socket event on the underlying http.ClientRequest to enforce a strict connection deadline:

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

async function makeRequestWithCustomTimeouts(url, options = {}) {
  const {
    connectionTimeout = 3000, // 3 seconds to connect
    socketTimeout = 5000,     // 5 seconds between data packets
    ...axiosConfig
  } = options;

  const httpAgent = new http.Agent({ keepAlive: true, timeout: socketTimeout });
  const httpsAgent = new https.Agent({ keepAlive: true, timeout: socketTimeout });

  const instance = axios.create({
    httpAgent,
    httpsAgent,
    ...axiosConfig
  });

  // Intercept request to monitor socket connection state
  instance.interceptors.request.use((config) => {
    config.transformRequest = [
      (data, headers) => {
        return data;
      }
    ];
    return config;
  });

  const response = await instance.get(url, {
    transport: {
      request: (options, callback) => {
        const req = (options.protocol === 'https:' ? https : http).request(options, callback);

        let connectionTimer = setTimeout(() => {
          req.destroy(new Error(`Connection timeout: Exceeded ${connectionTimeout}ms during connection phase`));
        }, connectionTimeout);

        req.on('socket', (socket) => {
          // Check if socket is already connected (e.g., reused via keepAlive)
          if (socket.connecting) {
            socket.once('connect', () => {
              clearTimeout(connectionTimer);
            });
            socket.once('secureConnect', () => {
              clearTimeout(connectionTimer);
            });
          } else {
            clearTimeout(connectionTimer);
          }

          // Handle socket inactivity
          socket.setTimeout(socketTimeout, () => {
            req.destroy(new Error(`Socket timeout: Inactivity exceeded ${socketTimeout}ms`));
          });
        });

        return req;
      }
    }
  });

  return response;
}

Using AbortController for Connection Timeouts

For a lightweight implementation without modifying underlying transport layers, you can use AbortController to handle the total connection phase independently:

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

async function fetchWithTimeouts(url) {
  const controller = new AbortController();
  const connectionTimeoutMs = 3000;
  const socketTimeoutMs = 7000;

  const httpAgent = new http.Agent({ timeout: socketTimeoutMs });
  const httpsAgent = new https.Agent({ timeout: socketTimeoutMs });

  const timeoutId = setTimeout(() => {
    controller.abort(new Error(`Connection timed out after ${connectionTimeoutMs}ms`));
  }, connectionTimeoutMs);

  try {
    const response = await axios.get(url, {
      signal: controller.signal,
      httpAgent,
      httpsAgent,
      onDownloadProgress: () => {
        // Clear the connection timeout once data starts streaming
        clearTimeout(timeoutId);
      }
    });

    clearTimeout(timeoutId);
    return response.data;
  } catch (error) {
    clearTimeout(timeoutId);
    throw error;
  }
}

Summary of Configuration Methods

Timeout Type Mechanism in Node.js / Axios Target Failure Mode
Total Request Timeout Axios timeout option Entire request/response takes too long.
Socket Timeout http.Agent({ timeout }) or socket.setTimeout() Data flow stops or hangs after connecting.
Connection Timeout Socket connection listeners or AbortController Server is unreachable, dropping SYN packets, or failing TLS negotiation.