Axios Proxy Not Working: 16 Causes and Fixes (Tested)

Axios skips your proxy or fails through it in Node.js? 16 causes with tested fixes: proxy: false in 1.21, HTTP/2, CONNECT, 407, NO_PROXY, redirects, SOCKS5.

On this page

When an Axios request in Node.js skips the proxy, or fails on its way through one, the cause is one of the 16 below, and most of them depend on the Axios version. Axios 1.21.0 and 1.21.1 ignore a proxyEnv agent when proxy: false is set. httpVersion: 2 and the fetch adapter go direct on every version that has them. Releases before 1.16.1 never open a CONNECT tunnel for an https:// URL. Each cause comes with the output we recorded and a fix we ran.

Tested on Node.js 24.22.2 and 26.13.0 with Axios 1.12.2, 1.19.0, 1.20.0, 1.21.0 and 1.21.1, plus 1.13.6, 1.15.0, 1.16.0, 1.16.1 and 1.18.1 to find where behaviour changed, https-proxy-agent 9.1.0 and socks-proxy-agent 10.1.0, on macOS 26.5.1 arm64, 11 November 2026. The lab is a logging proxy and an HTTPS target on 127.0.0.1. "Bypassed" means the target answered and the proxy logged nothing. The scripts, the results table and all 1,290 records are in the lab folder. To set a proxy up for the first time, start with Axios proxy setup; this page is for a setup that doesn't behave.

Quick check: is the request using the proxy?

js
import axios from 'axios';

// Use the same client and options as the code you are debugging.
const res = await axios.get('https://api.ipify.org?format=json', {
  proxy: { protocol: 'http', host: 'proxy.example', port: 8080 },
});
console.log(res.data);

Run it with the options of the failing code, then once more with no proxy at all. If both runs print the same IP, the request went around the proxy. A 200 proves nothing: every bypass on this page returned 200. If you can read the proxy's own log, look there first. In the lab each proxied https:// request left one CONNECT line, and each bypassed one left none.

Find your symptom

What you seeGo to
Status 200, but the IP is your own and the proxy logs nothingThe request ignores the proxy
400, socket hang up, wrong version number, ENOTFOUND undefined, or a request that never returnsHTTPS through an HTTP proxy fails
Request failed with status code 407407 and authentication errors
HTTPS_PROXY is set but ignored, or a host is skipped that shouldn't beEnvironment variables and NO_PROXY
The first request is proxied and the redirect is notRedirects leave the proxy
protocol mismatch, or a SOCKS proxy that can't resolve the hostSOCKS5 proxies

The request ignores the proxy, with no error

1. proxy: false next to a proxyEnv agent (Axios 1.21.0 and later)

js
import https from 'node:https';
import axios from 'axios';

const agent = new https.Agent({ proxyEnv: { https_proxy: 'http://proxy.example:8080' } });
const res = await axios.get('https://example.com/', { proxy: false, httpsAgent: agent });
console.log(res.status);

On Axios 1.20.0 the lab proxy carried this request. On 1.21.0 the same script printed:

code
200
# proxy log: (nothing)
# target log: GET /ip HTTP/1.1 direct

Since 1.21.0, proxy: false means "connect directly" even when the agent has its own proxy settings: Axios replaces the agent with a copy that has no proxyEnv (#11402, closed on 5 November 2026; the change is PR #11242, commit 2498c23). The same happened in the lab with Node's own switch: NODE_USE_ENV_PROXY=1 or --use-env-proxy plus proxy: false went through the proxy up to 1.20.0 and direct on 1.21.0 and 1.21.1.

Fix: remove proxy: false from requests that use such an agent. The reporter of #11402 confirmed it, and in the lab the script without it went through the proxy on all ten versions. Axios leaves environment proxying to an agent that has proxyEnv (request config docs). Keep proxy: false next to a third-party agent such as HttpsProxyAgent: that pair worked on every version (cause 5).

2. httpVersion: 2

js
import axios from 'axios';

// HTTPS_PROXY=http://proxy.example:8080 is set in the environment.
const res = await axios.get('https://example.com/', { httpVersion: 2 });
console.log(res.status);

On Axios 1.21.1:

code
200
# proxy log: (nothing)
# target log: GET /ip HTTP/2.0 direct
Axiosproxy objectHTTPS_PROXYproxy: false + HttpsProxyAgent
1.13.6, 1.15.0, 1.16.0ERR_HTTP2_ERRORERR_HTTP2_ERRORbypassed silently
1.16.1, 1.18.1, 1.19.0bypassed silentlybypassed silentlybypassed silently
1.20.0, 1.21.0, 1.21.1ERR_NOT_SUPPORTbypassed silentlybypassed silently

Axios 1.12.2 has no HTTP/2 support and ignored the option. The security advisory GHSA-3pq3-5fj3-cg6v (CVE-2026-101898, GitLab entry) covers 1.13.0 up to, but not including, 1.20.0: the HTTP/2 path connects on its own and drops the proxy. The lab reproduced the silent bypass on 1.16.1 to 1.19.0. On 1.13.6, 1.15.0 and 1.16.0 the request failed with Protocol error after sending HTTP/2 bytes to the proxy, so there it was loud, not silent.

The fix in 1.20.0 rejects an explicit proxy object with HTTP/2 requests with a proxy are not supported. It does not cover the environment: PR #11141 says HTTP/2 stays direct when the proxy comes only from environment variables or an agent, and the docs say HTTP/2 requests cannot be proxied. NODE_USE_ENV_PROXY=1 went direct too.

Fix: remove httpVersion: 2 from every request that must use the proxy. The same request without it went through the proxy over HTTP/1.1 on 1.16.1 and later.

3. The fetch adapter

With adapter: 'fetch', a proxy object, HTTPS_PROXY and an httpsAgent were each bypassed silently on all ten versions and both Node lines. The docs mark proxy as Node.js only, and in the lab only the default HTTP adapter read it, the proxy variables or the agents.

Fix: remove adapter: 'fetch', or let Node proxy its own fetch: with NODE_USE_ENV_PROXY=1 and HTTPS_PROXY set, the fetch adapter went through the proxy on Node 24.22.2 and 26.13.0. proxy: false did not switch that off. If the error is fetch failed instead, see fix Node.js fetch failed behind a proxy and invalid onError method on Node.js 26.

4. The agent is in the wrong option

An HttpsProxyAgent passed as httpAgent only, with an https:// URL: bypassed silently on every version. Axios uses httpAgent for http:// requests and httpsAgent for https:// requests (README).

Fix: set both options, as the SOCKS example below does.

HTTPS through an HTTP proxy fails

5. Axios before 1.16.1 sends no CONNECT

With a proxy object or HTTPS_PROXY, Axios 1.12.2, 1.13.6, 1.15.0 and 1.16.0 did not ask the proxy for a tunnel. The lab proxy received the whole URL in a plain request:

code
AxiosError: Request failed with status code 400
# exit code: 1
# proxy log: A GET https://target.test:<T>/ip -> 400
# target log: (nothing)

Our proxy refuses such a request with 400. A lab proxy that drops the connection produced socket hang up (ECONNRESET), the error reported in #4531. That issue was opened in March 2022 and closed on 30 July 2025 in a cleanup that asked reporters to retest. #3384, closed in June 2022, describes the same setup with a proxy-specific handshake error that we could not reproduce.

Axios 1.16.1 changed this: PR #10858 makes Axios use a CONNECT tunnel for HTTPS targets "instead of forwarding request URLs, headers, and bodies to the proxy in plaintext". From 1.16.1 on, every one of these cases tunnelled.

Fix: upgrade to 1.16.1 or later. If you can't, use a proxy agent and switch Axios's own proxy handling off. This worked on all ten versions, 1.12.2 included:

js
import axios from 'axios';
import { HttpsProxyAgent } from 'https-proxy-agent';

const agent = new HttpsProxyAgent('http://proxy.example:8080');
const res = await axios.get('https://example.com/', {
  proxy: false, // Axios adds no proxy handling of its own
  httpsAgent: agent, // the agent opens the CONNECT tunnel
  signal: AbortSignal.timeout(15000),
});
console.log(res.status);

6. wrong version number: the proxy scheme is wrong or missing

code
write EPROTO <id>:error:0A00010B:SSL routines:tls_validate_record_header:wrong version number:../deps/openssl/openssl/ssl/record/methods/tlsany_meth.c:78:

The client is speaking TLS to a proxy port that speaks plain HTTP. On every version the lab got this error from protocol: 'https' in the proxy object, from HTTPS_PROXY=https://127.0.0.1:8080, from HTTPS_PROXY=127.0.0.1:8080 with no scheme at all, and from new HttpsProxyAgent('https://…'). Up to 1.16.0, a proxy object with no protocol failed the same way; from 1.16.1 it worked.

protocol and the URL scheme describe the connection to the proxy, not to the site: use https only when the proxy itself serves TLS (README). #6198 reports this error with an HttpsProxyAgent; it was closed in August 2025 for lack of a reproduction, and its configuration with an http:// proxy URL worked in our lab.

Fix: write protocol: 'http', or http:// in the URL, for an ordinary HTTP proxy. SSL: wrong version number through a proxy covers the same error in other clients.

7. The proxy is a string, or the scheme is inside host

  • proxy: 'http://proxy.example:8080' (a string): getaddrinfo ENOTFOUND undefined from 1.16.1, connect ECONNREFUSED ::1:443 before it.
  • proxy: { host: 'http://proxy.example', port: 8080 }: Invalid URL (ERR_INVALID_URL) from 1.16.1, getaddrinfo ENOTFOUND http://127.0.0.1 before it.

Neither reached the proxy. #2072, closed in 2020, has the second form in browser code, where the proxy option does not apply at all (docs); we did not test browsers.

Fix: pass an object with a bare hostname in host, a numeric or string port and the scheme in protocol.

8. The request never returns

From Axios 1.16.1, with a proxy object or HTTPS_PROXY, a proxy that closed the connection after CONNECT, or never answered it, left the request pending: no response and no error after 15 seconds, although timeout: 5000 was set. Before 1.16.1 the same cases rejected with socket hang up or timeout of 5000ms exceeded. With proxy: false and an HttpsProxyAgent, a closed connection rejected with Proxy connection ended before receiving CONNECT response, and a silent proxy left the request pending on all ten versions. http:// URLs rejected normally, with timeout of 5000ms exceeded or socket hang up. We found no Axios issue for this on 11 November 2026.

Fix: give every proxied request a deadline of its own. signal: AbortSignal.timeout(3000) ended every pending case after 3 seconds with canceled (ERR_CANCELED). maxRedirects: 0 also made timeout work for the built-in tunnel, a case the 1.16.0 notes mention.

407 and authentication errors

A wrong password, or none, returned the same thing on every version, whether the credentials came from proxy.auth, from HTTPS_PROXY or from an HttpsProxyAgent URL: Request failed with status code 407, with error.code set to ERR_BAD_REQUEST and error.response.status to 407. The proxy logged one request, answered with 407, and the target saw nothing. A SOCKS5 proxy has no status code: a wrong password there gave Socks5 Authentication failed.

9. The credentials are in the wrong place

Putting the proxy's username and password in the top-level auth option returned 407, and the proxy log shows it received no credentials. Top-level auth is for the site; the proxy's credentials go in proxy.auth (README). With proxy.auth, from 1.16.1 the proxy authenticated the CONNECT and the target never saw a Proxy-Authorization header, on a redirect to a second host too.

10. Special characters encoded the wrong way

The lab proxy expected the password p@ss/w0rd#1:

Where the password goesWrittenResult
proxy.auth.passwordas it isthrough the proxy (1.16.1 and later)
proxy.auth.passwordpercent-encoded407: the proxy received p%40ss%2Fw0rd%231
HTTPS_PROXY URLas it isgetaddrinfo ENOTFOUND ss
HTTPS_PROXY URLpercent-encoded407: the proxy received p%40ss%2Fw0rd%231
HttpsProxyAgent URLpercent-encodedthrough the proxy (all versions)

Pasted as it is, the @ ends the credentials early and ss becomes the host. Percent-encoded, the URL parses, but Axios copies the username and password out of it without decoding them (http.js in 1.21.1). So on the tested versions a password with reserved characters could not go through HTTPS_PROXY at all.

Fix: pass it in proxy.auth unencoded, or in an agent URL percent-encoded. Both requests below went through the proxy on 1.19.0 to 1.21.1:

js
import axios from 'axios';
import { HttpsProxyAgent } from 'https-proxy-agent';

const user = process.env.PROXY_USER;
const pass = process.env.PROXY_PASS;

// In the proxy object: the raw password, not percent-encoded.
const a = await axios.get('https://example.com/', {
  proxy: { protocol: 'http', host: 'proxy.example', port: 8080, auth: { username: user, password: pass } },
});

// In a proxy URL for an agent: percent-encode both parts.
const url = `http://${encodeURIComponent(user)}:${encodeURIComponent(pass)}@proxy.example:8080`;
const b = await axios.get('https://example.com/', { proxy: false, httpsAgent: new HttpsProxyAgent(url) });
console.log(a.status, b.status);

For a 407 that survives correct credentials, such as an IP allowlist or a username format, see fix proxy error 407.

Environment variables aren't picked up, or NO_PROXY matches wrongly

What Axios read in the lab, on all ten versions: HTTPS_PROXY or https_proxy for https:// URLs, HTTP_PROXY or http_proxy for http:// URLs, and ALL_PROXY or all_proxy when the specific one is missing. With both spellings set, lowercase won. The variables are read at request time, so assigning process.env.HTTPS_PROXY in code before the request worked. A proxy object beat both the variables and NO_PROXY. Before 1.16.1 the variables were read the same way, but https:// requests then failed as in cause 5.

11. The variable doesn't match the URL scheme

With only HTTP_PROXY set, an https:// request went direct with no error, on every version. With only HTTPS_PROXY, an http:// request did.

Fix: set both, usually to the same http:// proxy URL. Proxy environment variables compares how other clients read them.

12. An environment proxy overrides your agent

With HTTPS_PROXY set, an HttpsProxyAgent or SocksProxyAgent passed as httpsAgent without proxy: false was ignored: from 1.16.1 the request went through the proxy from the environment, not the agent's. A proxy object next to the agent won in the same way. A CI runner or a container that sets HTTPS_PROXY is enough to send your traffic through a different proxy than the one in your code.

Fix: proxy: false on every request that uses a third-party proxy agent. With it, the agent's proxy carried the request on all ten versions, whatever the environment held.

13. NO_PROXY matches differently across versions

With a proxy variable set and the URL host target.test:

NO_PROXYRouteVersions
target.test, TARGET.TEST, .test, *.test, *, target.test:PORT with the URL's portdirectall
other.test target.test or other.test, target.testdirectall
.target.test, test, get.test, target.test:9999, https://target.testproxyall
target.test, when the URL says target.test. with a trailing dotproxy on 1.12.2 and 1.13.6, direct from 1.15.0fixed in 1.15.0
127.0.0.0/8, URL host 127.0.0.1proxy up to 1.19.0, direct from 1.20.0fixed in 1.20.0
localhost, URL host 127.0.0.1proxy up to 1.15.0, direct from 1.16.0changed in 1.16.0

The trailing-dot row is GHSA-3p68-rc4w-qgx5, fixed in 1.15.0. The CIDR row is GHSA-44g4-m2mj-wpvx (CVE-2026-101899), fixed in 1.20.0; the advisory lists 1.15.0 and later as affected, and in the lab 1.12.2 and 1.13.6 ignored the CIDR entry as well. With NO_PROXY unset, requests to localhost and 127.0.0.1 went through the proxy on every version. NO_PROXY matching, tested has the same matrix for other clients.

Redirects leave the proxy

14. A redirect changes scheme, or lands on a NO_PROXY host

The lab target answers 302 to a second host.

  • http:// to https://, only HTTP_PROXY set: the first hop went through the proxy and the second went direct, on every version. The same happened with proxy: false and only httpAgent set. With both agents, or from 1.16.1 with both variables, both hops were proxied.
  • https:// to https:// on another host: both hops went through the proxy with a proxy object, with HTTPS_PROXY and with an agent, from 1.16.1. With proxy authentication, both CONNECT requests carried the credentials.
  • Redirect to a host listed in NO_PROXY: on 1.21.0 and 1.21.1 the second hop went direct. On 1.16.1 to 1.20.0 it still went through the proxy. With an explicit proxy object both hops were proxied from 1.16.1 on.

Fix: set both variables or both agents. If a redirect must never leave the proxy, use a proxy object or an agent pair with proxy: false, and keep that host out of NO_PROXY.

SOCKS5 proxies

15. socks5:// in the proxy option or HTTPS_PROXY

Axios has no SOCKS client. proxy: { protocol: 'socks5', … } and HTTPS_PROXY=socks5://… failed with protocol mismatch (ERR_ASSERTION) up to 1.16.0. From 1.16.1 Axios sent an HTTP CONNECT line to the SOCKS port, the lab's SOCKS server closed the connection, and the request never returned (cause 8).

Fix: a SOCKS agent in both agent options, with proxy: false (cause 12). This went through the lab's SOCKS5 proxy on all ten versions:

js
import axios from 'axios';
import { SocksProxyAgent } from 'socks-proxy-agent';

// socks5h: the proxy resolves the hostname. socks5: this machine does.
const agent = new SocksProxyAgent('socks5h://proxy.example:1080');
const res = await axios.get('https://example.com/', {
  proxy: false,
  httpAgent: agent,
  httpsAgent: agent,
  signal: AbortSignal.timeout(15000),
});
console.log(res.status);

16. socks5 instead of socks5h

With socks5://, the lab's SOCKS server received an IP address: the client had resolved the name itself. With socks5h:// it received the hostname (socks-proxy-agent README). For a name that only the proxy can resolve, socks5:// failed with getaddrinfo ENOTFOUND proxy-only.test and socks5h:// worked. SOCKS5 vs SOCKS5h explains what else the local lookup gives away.

A known-good setup for each need

All three went through the lab proxy on Axios 1.19.0, 1.20.0, 1.21.0 and 1.21.1, on both Node lines. On 1.12.2 only the third did.

A fixed proxy in code (Axios 1.16.1 or later):

js
import axios from 'axios';

const client = axios.create({
  proxy: {
    protocol: 'http', // how to reach the proxy, not the site
    host: 'proxy.example',
    port: 8080,
    auth: { username: process.env.PROXY_USER, password: process.env.PROXY_PASS },
  },
});

const res = await client.get('https://example.com/', { signal: AbortSignal.timeout(15000) });
console.log(res.status);

A company proxy from the environment (Axios 1.16.1 or later). Set the variables outside the code:

sh
export HTTPS_PROXY=http://proxy.example:8080
export HTTP_PROXY=http://proxy.example:8080
export NO_PROXY=localhost,127.0.0.1
js
import axios from 'axios';

// Started with HTTPS_PROXY, HTTP_PROXY and NO_PROXY in the environment.
// No proxy option, no agent, no httpVersion, default adapter.
const res = await axios.get('https://example.com/', { signal: AbortSignal.timeout(15000) });
console.log(res.status);

A different proxy per request (every tested version):

js
import axios from 'axios';
import { HttpsProxyAgent } from 'https-proxy-agent';

const agents = new Map();
const agentFor = (proxyUrl) => {
  if (!agents.has(proxyUrl)) agents.set(proxyUrl, new HttpsProxyAgent(proxyUrl));
  return agents.get(proxyUrl);
};

const viaProxy = (url, proxyUrl) =>
  axios.get(url, { proxy: false, httpsAgent: agentFor(proxyUrl), signal: AbortSignal.timeout(15000) });

const first = await viaProxy('https://example.com/', 'http://proxy-a.example:8080');
const second = await viaProxy('https://example.com/', 'http://proxy-b.example:8080');
console.log(first.status, second.status);

In the lab the two requests left through two different proxies. For http:// URLs add an HttpProxyAgent from http-proxy-agent as httpAgent.

A guard for your tests

Most causes above return 200, so a normal test suite stays green when an upgrade or a new option starts bypassing the proxy. This test starts a stand-in proxy that records what reaches it and refuses it, then sends one request through the client your application builds:

js
import assert from 'node:assert/strict';
import { once } from 'node:events';
import http from 'node:http';
import { test } from 'node:test';
import { createClient } from './client.mjs';

test('the HTTP client sends requests through the proxy', async () => {
  const seen = [];
  // A stand-in proxy: it records what arrives and refuses it, so nothing leaves the machine.
  const proxy = http.createServer((req, res) => {
    seen.push(`${req.method} ${req.url}`);
    res.writeHead(503).end();
  });
  proxy.on('connect', (req, socket) => {
    seen.push(`CONNECT ${req.url}`);
    socket.end('HTTP/1.1 503 Service Unavailable\r\n\r\n');
  });
  proxy.listen(0, '127.0.0.1');
  await once(proxy, 'listening');

  const client = createClient(`http://127.0.0.1:${proxy.address().port}`);
  // .invalid never resolves, so a request that skips the proxy cannot reach a real site.
  await client.get('https://guard.invalid/', { signal: AbortSignal.timeout(5000) }).catch(() => {});
  proxy.close();

  assert.deepEqual(seen, ['CONNECT guard.invalid:443']);
});

createClient(proxyUrl) is your own factory. With the per-request client above, node --test passed on all five main versions. After adapter: 'fetch' was added to the same client, it failed:

code
not ok 1 - the HTTP client sends requests through the proxy
    Expected values to be strictly deep-equal:
    + actual - expected
    + []
    - [
    -   'CONNECT guard.invalid:443'
    - ]
# tests 1
# pass 0
# fail 1
# exit code: 1

Versions to use or avoid

  • Use 1.21.1, the latest release on the test date, or at least 1.20.0. Check two things after the upgrade: proxy: false next to a proxyEnv agent (cause 1), and redirects to NO_PROXY hosts (cause 14).
  • Before 1.16.1: no CONNECT tunnel for https:// URLs through an HTTP proxy (cause 5). Use the agent pattern or upgrade.
  • 1.13.0 up to 1.19.0 with httpVersion: 2: inside the range of GHSA-3pq3-5fj3-cg6v. On 1.20.0 and later HTTP/2 still ignores HTTPS_PROXY (cause 2).
  • Before 1.15.0 and before 1.20.0: NO_PROXY misses trailing-dot hosts and CIDR ranges respectively (cause 13).
  • Node.js: 24.22.2 and 26.13.0 routed every case the same way. The one difference: through the fetch adapter, Node 26 negotiated HTTP/2 with the lab target and Node 24 used HTTP/1.1.

FAQ

Why does Axios ignore HTTPS_PROXY?

In the lab it did so for six reasons: proxy: false on the request, httpVersion: 2, adapter: 'fetch', a proxy object that takes precedence, a NO_PROXY match, or an http:// URL, which reads HTTP_PROXY instead.

Do I still need https-proxy-agent with Axios?

Not for a single HTTP proxy on Axios 1.16.1 or later: the proxy object and HTTPS_PROXY tunnelled on their own. You need it on older versions, and it is the simplest way to use a different proxy per request.

Does Axios support SOCKS5 proxies?

Not through proxy or HTTPS_PROXY. Pass a SocksProxyAgent as httpAgent and httpsAgent with proxy: false.

Why does my Axios request hang through a proxy when timeout is set?

On the tested versions timeout did not cover a proxy that fails to answer CONNECT (cause 8). Add signal: AbortSignal.timeout(ms).

Does the Axios proxy option work in the browser?

No. The docs mark proxy as Node.js only; a browser uses the proxy of the operating system or the browser itself.

Test the fix through a real IP. ipvolt sells residential proxies from $2.20 per GB and a dedicated mobile proxy from $9 a day. Put the proxy in the setup above and run the quick check. Check a proxy now.

Sources & further reading

Technical references used for this guide. Check the documentation for your installed version and your provider’s supported configuration.