All guides
DEVELOPER GUIDES · 6 MIN READ

Axios Proxy Guide: Configure HTTP CONNECT, Timeouts, and Bounded Retries

Use Axios Node proxy configuration with secret-safe authentication, response limits, phase-aware errors, and a bounded GET retry policy.

Axios gives Node applications a direct proxy configuration, but the useful result still depends on more than a response status. You need to decide which proxy protocol is allowed, where credentials come from, how long a request may run, how much body data to buffer, and which failures are safe to repeat.

The example sends one idempotent GET to https://example.com/, then checks both status and content. Replace the URL and marker with a permitted target and the field your worker must accept. The request reports only a status and response size; it never prints the proxy object or raw Axios error.

Build an explicit proxy object

Axios's Node request config documents proxy with a protocol, host, port, and HTTP Basic auth. For an HTTPS target, Axios establishes a CONNECT tunnel through the proxy and keeps TLS with the origin. A plain HTTP proxy endpoint is a normal choice for that tunnel; use an HTTPS proxy protocol only when the intermediary supports TLS itself.

Inject these values through your runtime secret binding. The password never appears in source, a process argument, or a log:

import axios from "axios";

const target = "https://example.com/";
const proxyProtocol = process.env.PROXY_PROTOCOL ?? "http";
const proxyHost = process.env.PROXY_HOST;
const proxyPort = Number(process.env.PROXY_PORT);
const proxyUser = process.env.PROXY_USER;
const proxyPassword = process.env.PROXY_PASSWORD;

if (
  !["http", "https"].includes(proxyProtocol)
  || !proxyHost
  || !Number.isInteger(proxyPort)
  || proxyPort < 1
  || proxyPort > 65_535
) {
  throw new Error("proxy configuration is invalid");
}

const proxy = {
  protocol: proxyProtocol,
  host: proxyHost,
  port: proxyPort,
  ...(proxyUser !== undefined && proxyPassword !== undefined
    ? { auth: { username: proxyUser, password: proxyPassword } }
    : {}),
};

const client = axios.create({
  timeout: 15_000,
  maxContentLength: 1_000_000,
  maxBodyLength: 1_000_000,
  maxRedirects: 0,
  validateStatus: () => true,
});

try {
  const response = await client.get(target, {
    proxy,
    responseType: "text",
  });
  if (response.status === 407) {
    throw new Error("proxy authentication or CONNECT negotiation failed");
  }
  if (response.status !== 200) {
    throw new Error(`target returned HTTP ${response.status}`);
  }
  if (!response.data.includes("Example Domain")) {
    throw new Error("accepted status, but the expected marker was absent");
  }
  console.log({ status: response.status, responseDataCharacters: response.data.length });
} catch (error) {
  if (
    error instanceof Error
    && (error.message.startsWith("target returned HTTP")
      || error.message.startsWith("proxy authentication"))
  ) {
    if (error.message.startsWith("proxy authentication")) {
      throw new Error("request failed (proxy_auth_or_407)");
    }
    throw error;
  }
  throw new Error("request failed");
}

Axios also documents conventional http_proxy, https_proxy, and no_proxy environment settings. An explicit proxy object keeps this request's route reviewable; proxy: false disables proxy use and ignores those environment variables when that is the intended policy. Do not assume that Axios's HTTP proxy object authenticates a SOCKS endpoint. Use a documented protocol-specific agent for another proxy protocol, and verify its current API separately.

Cap time and response data

Axios's timeout is expressed in milliseconds and aborts a request that exceeds it. maxContentLength caps response data and maxBodyLength caps request data in the Node adapters; both default to unlimited, so an application calling an untrusted target should set an explicit cap. The example uses text because it validates a small marker. For binary or streaming data, choose a response type and consumption policy that preserves your byte budget.

An Axios timeout is per request. It does not set a total deadline for a queue item that can be attempted several times. Put a job deadline around the whole operation, and keep each retry delay inside that remaining budget. If you need cancellation from a parent task, Axios also accepts an AbortSignal through signal.

Retry a narrow set of outcomes

Axios resolves an HTTP response when validateStatus returns true, including 407 and 5xx in this example. A 407 received during HTTPS CONNECT is a proxy-authentication or negotiation result, so classify it before calling it a target response. Axios exposes a 407 as a response status; label it proxy_auth_or_407 unless independent evidence shows that the adapter received it from the origin. Network failures still reject. Retry only selected connection codes for this safe GET, and retry 502/503/504 at most twice after the first attempt:

const RETRY_STATUSES = new Set([502, 503, 504]);
const RETRY_CODES = new Set(["ECONNABORTED", "ETIMEDOUT", "ECONNRESET", "ECONNREFUSED"]);

function boundedRetryDelay(value, attempt) {
  if (value === undefined) {
    return Math.min(500 * 2 ** attempt, 2_000);
  }
  const seconds = Number(value);
  if (!Number.isFinite(seconds) || seconds < 0 || seconds > 2) {
    return null;
  }
  return seconds * 1_000;
}

async function getWithRetries(url, proxy, maxAttempts = 3) {
  for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
    let response;
    try {
      response = await client.get(url, {
        proxy,
        responseType: "text",
      });
    } catch (error) {
      if (!axios.isAxiosError(error) || !RETRY_CODES.has(error.code) || attempt + 1 === maxAttempts) {
        throw new Error("request failed");
      }
      await new Promise((resolve) => setTimeout(resolve, Math.min(500 * 2 ** attempt, 2_000)));
      continue;
    }

    if (RETRY_STATUSES.has(response.status) && attempt + 1 < maxAttempts) {
      const delay = boundedRetryDelay(response.headers["retry-after"], attempt);
      if (delay === null) {
        return response;
      }
      await new Promise((resolve) => setTimeout(resolve, delay));
      continue;
    }
    if (response.status === 407) {
      return { phase: "proxy_auth_or_407", status: response.status, data: null };
    }
    return response;
  }
  throw new Error("request exhausted its bounded retries");
}

The helper does not retry HTTP 407, 429, or a malformed Retry-After value. It returns 407 as proxy_auth_or_407 so the caller does not label a CONNECT challenge as an origin response. A date-form Retry-After is left for the scheduler or worker policy rather than guessed as a short delay. A proxy authentication failure needs corrected credentials or endpoint settings; repeating it can add load without changing the result. Changing proxy exits to push through a target rate limit is not an acceptable retry policy.

After the helper returns, accept the result only when the status is in the range your workflow expects and required fields are present. Never serialize error.config, error.request, or the full response into an unredacted log: those objects can contain proxy configuration or target credentials. Record a phase, status, attempt number, and a stable target identifier instead.

For an established route, ProxyLane offers HTTP/SOCKS5 traffic from $2.50/GB, with country, city, ISP, rotating, and sticky options. Traffic does not expire; registration is free and the team follows up about access. Payment is separate. Request proxy access after you have an authorized target and an acceptance rule.

If the application is Python-based, compare the aiohttp proxy guide for async session pooling and response cleanup. The client differs, but the evidence chain is the same: explicit route, bounded work, proxy-versus-target status, and a validated output.

Sources and further reading

Request access

Keep reading

Amazon Scraper API vs Proxy: Choose a Product Data Source

Distinguish authorized Amazon APIs, licensed product data and proxy-based page checks by record quality and access rights.

Read guide

Apify Custom Proxy Setup: Use Your Own Residential Route

Connect a buyer-owned proxy to an Apify Actor, keep the session boundary clear, and validate records instead of counting requests.

Read guide

Australia Residential Proxies: Verify the AU Exit and State-Level Result

Separate Australian egress from en-AU content, AUD pricing, GST display, postcode validation and the state delivery context.

Read guide