Double Rewards

Rotating IPs:Buy 50GB, get 10GB free

Static IPs:Get 7 extra days for every 30 days purchased, plus 500MB per order

Buy Now >

Security AlertA fraudulent phishing site ( rola-ip.com) is impersonating our previous official website. ROLA IP's only official website is rola-ip.co (make sure it uses the .co domain suffix). To protect your funds, do not make payments on any other domain!

Axios Proxy in Node.js: HTTP, HTTPS, SOCKS5

Daniel Zhao

Sep 17, 2026 · Guides · 14 min read

To use axios with a proxy in Node.js, pass a proxy object with protocol, host, port, and an optional auth object to the request or to an axios instance. On axios 1.16.1 and later, the same setting works for HTTP and HTTPS targets: axios forwards plain HTTP requests and opens a CONNECT tunnel for HTTPS. For SOCKS5, use a routing agent such as socks-proxy-agent with proxy: false.

This guide is for Node.js developers who call APIs or collect data they are authorized to access and need the traffic to leave through a proxy. It uses Axios 1.21.0 with Node.js 22.22.0 or 24.22.1 and the Node.js HTTP adapter. Browser Axios and the fetch adapter use different networking paths and are outside this guide’s scope.

Quick answer

import axios from 'axios';

const response = await axios.get('https://example.com/', {
  proxy: {
    protocol: 'http',
    host: 'PROXY_HOST',
    port: 8000,
    auth: { username: 'PROXY_USERNAME', password: 'PROXY_PASSWORD' },
  },
  timeout: 15000,
  signal: AbortSignal.timeout(15000),
});

This snippet uses ES module syntax, so save it as a .mjs file or in a project with "type": "module". It also works in TypeScript. The longer examples below use CommonJS require.

  • protocol describes the connection to the proxy, not the scheme of the target URL. Use 'http' for an ordinary HTTP proxy even when the target is https://, and 'https' only when the proxy itself accepts TLS.
  • Axios also reads HTTP_PROXY, HTTPS_PROXY, and NO_PROXY when no proxy option is set.
  • proxy: false turns off axios’s own proxy selection for that request.
  • For SOCKS5, put a SocksProxyAgent on httpAgent and httpsAgent and set proxy: false.
  • For an IP-allowlisted proxy, leave out the auth object.

Before you start: versions and setup

Axios proxy support described here applies to server-side Node.js with axios’s HTTP adapter, which is the default in Node. Your proxy provider’s dashboard gives you the host, port, username, and password. The proxy password is usually separate from your dashboard login.

Rola IP documentation page showing where the proxy host, port, account name, and region parameters are generated, with connection values blurred

Install the packages used in this guide:

npm install [email protected] [email protected] [email protected]
npm ls axios https-proxy-agent socks-proxy-agent

npm ls axios prints the installed version. Use 1.21.0, the current release on npm as of October 28, 2026, or at least 1.20.0 for proxied traffic. Several published axios advisories involve proxies:

Issue Affected axios versions Fixed in
HTTPS request data sent in cleartext to an HTTP proxy (v1.16.1 release notes) 1.x before 1.16.1 1.16.1
CVE-2026-44486: a stale Proxy-Authorization header can be sent to a redirect target 1.0.0 to 1.15.x, and 0.x before 0.32.0 1.16.0 and 0.32.0
GHSA-3pq3-5fj3-cg6v: requests with httpVersion: 2 skip the configured proxy 1.13.0 and later, before 1.20.0 1.20.0
GHSA-44g4-m2mj-wpvx: CIDR entries in NO_PROXY are ignored 1.15.0 and later, before 1.20.0 1.20.0
GHSA-mghh-pgcx-3jjj: denial of service (ReDoS) in NO_PROXY matching, reachable through redirects 1.15.0 and later, before 1.20.0 1.20.0

Also check your lockfile for axios 1.14.1 and 0.30.4. Those two versions were published from a compromised maintainer account on March 31, 2026 and carried a malicious dependency, according to Snyk’s incident write-up.

Axios proxy authentication with an HTTP proxy

Axios proxy authentication with a username and password is the most common setup. Save this file as axios-proxy.cjs:

The examples below use PROXY_HOST, PROXY_PORT, PROXY_USERNAME, PROXY_PASSWORD, and TARGET_URL. Replace the placeholder values with your connection details. Save each later JavaScript example as a separate .cjs file and run it with node filename.cjs; the CA and IP-check examples additionally use ORIGIN_CA_FILE and IP_ECHO_URL.

const axios = require('axios');

const required = ['PROXY_HOST', 'PROXY_PORT', 'PROXY_USERNAME', 'PROXY_PASSWORD', 'TARGET_URL'];
for (const name of required) {
  if (!process.env[name]) throw new Error('Set ' + name + ' before running this example.');
}
const port = Number(process.env.PROXY_PORT);
if (!Number.isInteger(port) || port < 1 || port > 65535) {
  throw new Error('PROXY_PORT must be an integer from 1 to 65535.');
}
const target = new URL(process.env.TARGET_URL);
if (!['http:', 'https:'].includes(target.protocol)) {
  throw new Error('TARGET_URL must use http:// or https://.');
}

const proxy = {
  protocol: 'http',
  host: process.env.PROXY_HOST,
  port: Number(process.env.PROXY_PORT),
  auth: {
    username: process.env.PROXY_USERNAME,
    password: process.env.PROXY_PASSWORD,
  },
};

axios
  .get(process.env.TARGET_URL, { proxy, timeout: 15000, signal: AbortSignal.timeout(15000) })
  .then((res) => console.log(res.status, res.data))
  .catch((err) => {
    console.error('Request failed:', err.response ? err.response.status : err.code);
    process.exitCode = 1;
  });

Set the connection details and run it. Keep deployed credentials in a secret store and avoid saving them in shell history. In Bash or zsh:

export PROXY_HOST='YOUR_PROXY_HOST'
export PROXY_PORT='YOUR_PROXY_PORT'
export PROXY_USERNAME='YOUR_PROXY_USERNAME'
export PROXY_PASSWORD='YOUR_PROXY_PASSWORD'
export TARGET_URL='https://YOUR_AUTHORIZED_TARGET/'
node axios-proxy.cjs

In PowerShell:

$env:PROXY_HOST = 'YOUR_PROXY_HOST'
$env:PROXY_PORT = 'YOUR_PROXY_PORT'
$env:PROXY_USERNAME = 'YOUR_PROXY_USERNAME'
$env:PROXY_PASSWORD = 'YOUR_PROXY_PASSWORD'
$env:TARGET_URL = 'https://YOUR_AUTHORIZED_TARGET/'
node axios-proxy.cjs

A working setup prints the expected status and response body from your target. For a controlled endpoint designed to return 200, check that both the status and expected content match. To confirm routing, look for a forwarded request for an http:// target or a CONNECT request for an https:// target in the proxy’s access log. A 407 response means “Proxy Authentication Required”; check credentials and the proxy’s authentication requirements.

The auth object takes the username and password as plain strings, including characters such as @, :, and #. Do not URL-encode auth.password. The axios request config documentation lists protocol, host, port, and auth as the proxy fields.

Set both timeout and an abort signal. A request timeout alone may not cover every connection phase, including a proxy that accepts TCP but stalls during CONNECT. signal: AbortSignal.timeout(15000) adds a separate cancellation deadline. Create a fresh signal for each request; an expired signal cannot be reused.

Axios HTTPS proxy: how HTTPS requests reach an HTTP proxy

An axios HTTPS request through an HTTP proxy uses a CONNECT tunnel on axios 1.16.1 and later. The proxy sees the target host and port, and TLS with the target site starts inside that tunnel, so the proxy does not see the decrypted request. Plain http:// targets are forwarded as ordinary requests, and the proxy sees the full URL.

Earlier 1.x releases used forward-proxy handling for HTTPS targets. The v1.16.1 release notes describe the fix for HTTPS request data sent in cleartext to an HTTP proxy. The version boundary matters when following older examples that recommend a separate tunnelling agent.

Diagram showing an axios HTTP target forwarded by an HTTP proxy and an HTTPS target sent through a CONNECT tunnel, with origin TLS ending at the HTTPS origin

You do not need to install a separate routing package for this setup: the proxy option handles HTTP forwarding and HTTPS tunnelling.

If the target uses a certificate from a private or internal CA, the error appears inside the tunnel, for example DEPTH_ZERO_SELF_SIGNED_CERT or UNABLE_TO_VERIFY_LEAF_SIGNATURE. Supply that CA through a standard https.Agent and keep the proxy option for routing:

const axios = require('axios');
const fs = require('node:fs');
const https = require('node:https');

const originTlsAgent = new https.Agent({
  ca: fs.readFileSync(process.env.ORIGIN_CA_FILE),
});

axios
  .get(process.env.TARGET_URL, {
    proxy: {
      protocol: 'http',
      host: process.env.PROXY_HOST,
      port: Number(process.env.PROXY_PORT),
      auth: {
        username: process.env.PROXY_USERNAME,
        password: process.env.PROXY_PASSWORD,
      },
    },
    httpsAgent: originTlsAgent,
    timeout: 15000,
    signal: AbortSignal.timeout(15000),
  })
  .then((res) => console.log(res.status, res.data))
  .catch((err) => {
    console.error('Request failed:', err.response ? err.response.status : err.code);
    process.exitCode = 1;
  });

Set ORIGIN_CA_FILE to the PEM file of the CA you trust. A ca option replaces Node’s default trusted CAs for that agent, so use this agent only for internal targets, or pass ca: [...tls.rootCertificates, fs.readFileSync(process.env.ORIGIN_CA_FILE)] with require('node:tls') if the same agent also calls public sites. Node’s NODE_EXTRA_CA_CERTS environment variable adds a CA for the whole process instead. Do not set rejectUnauthorized: false to silence the error in production, because it turns off the check that you are talking to the intended server.

Use the HTTP/1.x adapter path for these proxy examples; do not add httpVersion: 2. Axios 1.20.0 introduced rejection of explicit proxy configuration on HTTP/2 requests. The axios request config documentation also explains that HTTP/2 ignores proxy environment variables. GHSA-3pq3-5fj3-cg6v documents how earlier affected HTTP/2 versions bypassed configured proxy controls.

Reuse proxy settings with an axios instance

An axios instance applies one proxy configuration to every request it sends. Any field set on a single request overrides the instance default for that request.

const axios = require('axios');

const client = axios.create({
  proxy: {
    protocol: 'http',
    host: process.env.PROXY_HOST,
    port: Number(process.env.PROXY_PORT),
    auth: {
      username: process.env.PROXY_USERNAME,
      password: process.env.PROXY_PASSWORD,
    },
  },
  timeout: 15000,
});

async function main() {
  const viaProxy = await client.get(process.env.TARGET_URL, { signal: AbortSignal.timeout(15000) });
  const direct = await client.get(process.env.TARGET_URL, { proxy: false, signal: AbortSignal.timeout(15000) });
  console.log('instance:', viaProxy.status, 'override:', direct.status);
}

main().catch((err) => { console.error('Request failed:', err.code); process.exitCode = 1; });

The first request inherits the instance’s proxy configuration. The second disables Axios’s own proxy handling. With Axios 1.21.0 and the standard Node.js agents, it also avoids Node’s native environment-proxy settings; a custom agent can still choose its own route.

Use HTTP_PROXY, HTTPS_PROXY, and NO_PROXY

Axios proxy environment variables work without code changes. When a request has no proxy option, axios reads HTTP_PROXY for http:// targets and HTTPS_PROXY for https:// targets, in uppercase or lowercase, and skips the proxy for hosts listed in NO_PROXY.

export HTTPS_PROXY='http://PROXY_USERNAME:PROXY_PASSWORD@PROXY_HOST:PROXY_PORT'
export NO_PROXY='localhost,internal.example'
node your-app.cjs

Three configuration details matter here:

  • NO_PROXY=localhost excludes localhost from environment-selected proxy routing. Axios 1.20.0 fixed support for CIDR entries such as 10.0.0.0/8 in its own proxy-exclusion handling.
  • Reserved characters in URL credentials need percent-encoding; unencoded delimiters can change URL parsing or produce ERR_INVALID_URL.
  • Axios 1.21.0’s own environment-proxy parser reads URL credentials without percent-decoding them. If your password contains reserved characters, use the explicit proxy object with a plain auth.password, or a routing agent that decodes URL credentials. Node’s native environment-proxy support is a separate path, so do not assume every parser handles credentials identically.

To make a single request ignore these variables, set proxy: false:

const axios = require('axios');

axios
  .get(process.env.TARGET_URL, { proxy: false, timeout: 15000, signal: AbortSignal.timeout(15000) })
  .then((res) => console.log(res.status, res.data))
  .catch((err) => { console.error('Request failed:', err.code); process.exitCode = 1; });

Node.js’s built-in HTTP and HTTPS agents support environment-based proxy routing from Node.js 22.21.0 and 24.5.0. Enable it at startup with NODE_USE_ENV_PROXY=1 or --use-env-proxy, as described in the Node.js HTTP proxy documentation. This is separate from fetch’s proxy support. On Axios 1.20.0, a native agent can still apply its proxy settings when proxy: false disables Axios’s own handling. Axios 1.21.0 adds direct-agent handling for this case. Confirm the route with access logs, especially if your application supplies custom agents.

Use https-proxy-agent instead of the proxy option

https-proxy-agent is a separate package that opens the CONNECT tunnel itself. It is useful when your code already passes agents around or when another library expects one. Give it the proxy URL with percent-encoded credentials, put it on httpsAgent, and set proxy: false so axios does not also apply its own proxy:

const axios = require('axios');
const { HttpsProxyAgent } = require('https-proxy-agent');

const user = encodeURIComponent(process.env.PROXY_USERNAME);
const pass = encodeURIComponent(process.env.PROXY_PASSWORD);
const agent = new HttpsProxyAgent(
  `http://${user}:${pass}@${process.env.PROXY_HOST}:${process.env.PROXY_PORT}`
);

axios
  .get(process.env.TARGET_URL, { httpsAgent: agent, proxy: false, timeout: 15000, signal: AbortSignal.timeout(15000) })
  .then((res) => console.log(res.status, res.data))
  .catch((err) => { console.error('Request failed:', err.code); process.exitCode = 1; });

This example uses https-proxy-agent 9.1.0. Percent-encode each credential before constructing the proxy URL; the agent decodes the URL credentials for proxy authentication.

Assigning HttpsProxyAgent only to httpsAgent routes https:// targets. This configuration does not set a proxy route for http:// targets, which use httpAgent. Keep proxy: false so Axios does not select a second proxy from the environment. If you need both target schemes through an HTTP proxy, Axios’s built-in proxy option provides one configuration for both.

Axios SOCKS5 proxy setup

An axios proxy for SOCKS5 needs an agent because the built-in proxy option supports HTTP-family proxies, not SOCKS. Setting protocol: 'socks5' on that option can produce a protocol-mismatch error. Use socks-proxy-agent instead:

const axios = require('axios');
const { SocksProxyAgent } = require('socks-proxy-agent');

const user = encodeURIComponent(process.env.PROXY_USERNAME);
const pass = encodeURIComponent(process.env.PROXY_PASSWORD);
const agent = new SocksProxyAgent(
  `socks5h://${user}:${pass}@${process.env.PROXY_HOST}:${process.env.PROXY_PORT}`
);

axios
  .get(process.env.TARGET_URL, {
    httpAgent: agent,
    httpsAgent: agent,
    proxy: false,
    timeout: 15000,
    signal: AbortSignal.timeout(15000),
  })
  .then((res) => console.log(res.status, res.data))
  .catch((err) => { console.error('Request failed:', err.code); process.exitCode = 1; });

This example uses socks-proxy-agent 10.1.0. Set both httpAgent and httpsAgent so http:// and https:// targets both use the SOCKS route.

The scheme controls where target DNS lookups happen: socks5h:// passes the target hostname to the SOCKS proxy, while socks5:// resolves it locally before connecting. Use socks5h:// when the target hostname should be resolved at the proxy.

Rotate proxies per request in axios

Axios does not rotate proxies by itself. You can rotate in your own code by choosing a different proxy object for each request:

const axios = require('axios');

const proxies = (process.env.PROXY_LIST || '')
  .split(',')
  .map((entry) => entry.trim())
  .filter(Boolean)
  .map((entry) => {
    const parts = entry.split(':');
    const [host, port] = parts;
    if (parts.length !== 2 || !host || /[\s/@]/.test(host) || !/^\d+$/.test(port || '')) {
      throw new Error('Each proxy must be a hostname or IPv4 address followed by :port.');
    }
    const numericPort = Number(port);
    if (!Number.isInteger(numericPort) || numericPort < 1 || numericPort > 65535) {
      throw new Error('Each proxy port must be an integer from 1 to 65535.');
    }
    return { protocol: 'http', host, port: numericPort };
  });

if (!proxies.length) {
  throw new Error('Set PROXY_LIST to one or more host:port pairs.');
}

const auth = {
  username: process.env.PROXY_USERNAME,
  password: process.env.PROXY_PASSWORD,
};

let next = 0;
function nextProxy() {
  const proxy = proxies[next % proxies.length];
  next += 1;
  return { ...proxy, auth };
}

async function main() {
  for (let i = 0; i < 4; i += 1) {
    const proxy = nextProxy();
    const res = await axios.get(process.env.TARGET_URL, { proxy, timeout: 15000, signal: AbortSignal.timeout(15000) });
    console.log(`request ${i + 1} via ${proxy.host}:${proxy.port} ->`, res.status);
  }
}

main().catch((err) => {
  console.error('Request failed:', err.response ? err.response.status : err.code);
  process.exitCode = 1;
});

Set PROXY_LIST to comma-separated host:port pairs, for example proxy-a.example:8000,proxy-b.example:8000, using your actual endpoints. This simple parser accepts hostnames or IPv4 addresses, not IPv6 literals. All endpoints in this example share the same credentials. The loop selects endpoints in order; different endpoints do not necessarily produce different exit IPs. Save the block as axios-rotate.cjs and run it after setting the variables:

export PROXY_LIST='YOUR_FIRST_PROXY_HOST:8000,YOUR_SECOND_PROXY_HOST:8000'
node axios-rotate.cjs

A provider-managed rotating gateway can remove the need for a list. With Rola IP residential proxies, copy the connection details generated for your selected region and session settings into the proxy configuration. Keep the generated username unchanged, including its routing parameters. The residential proxy setup documentation describes the configuration workflow.

If you use Node’s built-in fetch instead of Axios, the Node.js fetch proxy guide covers that setup.

Rotation changes the apparent source of a request; the target site’s terms and rate limits still apply.

Check that axios really used the proxy

To check that axios really used the proxy, call an IP echo endpoint from the same process, once with proxy: false and once with the proxy, and compare the two answers. Set IP_ECHO_URL to an endpoint you trust that returns the caller’s address:

const axios = require('axios');

const proxy = {
  protocol: 'http',
  host: process.env.PROXY_HOST,
  port: Number(process.env.PROXY_PORT),
  auth: {
    username: process.env.PROXY_USERNAME,
    password: process.env.PROXY_PASSWORD,
  },
};

async function main() {
  const url = process.env.IP_ECHO_URL;
  const direct = await axios.get(url, { proxy: false, timeout: 15000, signal: AbortSignal.timeout(15000) });
  const proxied = await axios.get(url, { proxy, timeout: 15000, signal: AbortSignal.timeout(15000) });
  console.log('direct :', JSON.stringify(direct.data));
  console.log('proxied:', JSON.stringify(proxied.data));
}

main().catch((err) => {
  console.error('Request failed:', err.response ? err.response.status : err.code);
  process.exitCode = 1;
});

If both responses show the same address, the IP echo check is inconclusive. Confirm that the endpoint returns the caller’s address, compare the result with the expected proxy exit, and check the proxy’s access log alongside the application’s configuration. A shared exit can produce identical results even when the routes differ.

Run the check inside the application process with its own configuration. If you use Axios 1.20.0, disable Node’s native environment-proxy setting for the direct baseline, or upgrade to 1.21.0. A proxy checker can help establish whether the proxy itself is reachable before you debug Axios, but cannot establish which route your application took.

Axios proxy not working: errors and fixes

When an axios proxy is not working, check two things first: whether the request succeeded, and whether the proxy logged it.

Troubleshooting flow for an axios proxy request that starts with whether the request succeeded and then checks the proxy log

Symptom Likely cause Fix
Request failed: 407 The proxy rejected the username or password Copy the credentials again from the provider dashboard and pass them in auth
407 only when using HTTP_PROXY or HTTPS_PROXY The password contains reserved characters and axios sent the percent-encoded form Use the proxy object with a plain auth.password
ERR_INVALID_URL An unencoded @, :, or # in a proxy URL Percent-encode credentials in URLs, or use the proxy object
ERR_ASSERTION with “protocol mismatch” protocol: 'socks5' in the native proxy option Use SocksProxyAgent on httpAgent and httpsAgent with proxy: false
DEPTH_ZERO_SELF_SIGNED_CERT after a successful CONNECT The target’s certificate is not trusted Add the target’s CA through https.Agent or NODE_EXTRA_CA_CERTS
ECONNREFUSED Nothing is listening at the proxy host and port, or a firewall blocks it Check the host, port, and outbound network policy
Request hangs The proxy or target stalled; a request timeout may not cover every connection phase Set both timeout and a fresh signal: AbortSignal.timeout(ms)
ERR_NOT_SUPPORT httpVersion: 2 on a proxied request Remove httpVersion: 2 for proxied requests
Proxy log shows nothing, request succeeded NO_PROXY, proxy: false, or another agent sent the request elsewhere Check those settings and confirm the exit with the IP echo test
Works locally, not on the server The server has different proxy variables Log whether HTTP_PROXY, HTTPS_PROXY, and NO_PROXY are set, never their values
Wrong exit country Incorrect routing parameters or a geolocation-database mismatch Check the generated settings and the provider’s expected exit before comparing geolocation sources
403 Forbidden The origin or an intermediary refused access Identify the responding server; check permissions, credentials, target rules, and request requirements
429 Too Many Requests A rate limit was reached Reduce request rate, honor Retry-After when present, and cap retries for idempotent requests
ENOTFOUND or EAI_AGAIN DNS resolution failed or was temporarily unavailable Identify whether the failed lookup concerns the proxy or target, then verify the resolver and hostname
ETIMEDOUT or ECONNABORTED A connection or request timed out Inspect the failing connection phase and proxy or origin logs before changing the proxy

Keep retries bounded. For idempotent GET requests, allow at most two retries after the initial attempt, use increasing delays, and honor Retry-After as either a delay in seconds or an HTTP date. Set an overall time budget; if the required wait exceeds it, stop and schedule the request later. A 403 or 407 calls for an access or configuration check before retrying. Rotation does not reset a target’s access rules or rate limits.

An HTTP 429 response calls for lower request rates and handling the server’s Retry-After value; the Axios 429 troubleshooting guide explains the response and retry controls.

Never log a full proxy URL while debugging. It usually contains the username and password, and it ends up in whatever collects your logs.

FAQs