Somewhere on Stack Overflow right now, someone is convinced Axios "silently breaks" on HTTPS proxies. It's one of the most repeated claims in Node.js proxy tutorials, and it does not describe the current release tested for this guide. I spun up a local test rig with real HTTP and HTTPS origins behind two proxies, and Axios 1.19.0 tunneled the HTTPS request through a proper CONNECT request instead of bypassing the proxy.
That doesn't mean the pain people describe is fake. Old Axios versions had real bugs (see issue #3384 and issue #4531 for the receipts), and recent Node releases add a second environment-proxy path that deserves careful configuration. This guide walks through what actually works in Axios 1.19.0 as of today, every method for wiring a proxy into your requests, a rotation pattern using interceptors that almost no tutorial shows, and a full error-to-fix table for when things go sideways anyway.
What Is an Axios Proxy (and Why Does It Matter in Node.js)?
A proxy, in the Axios context, is just an intermediary server sitting between your Node process and the target site. Your request goes to the proxy first, the proxy forwards it, and the target sees the proxy's IP address instead of yours. That's the whole trick.
Developers reach for this for a handful of reasons: scraping sites that rate-limit or block by IP, testing how an app behaves from a different geography, routing traffic through a corporate egress point, or just keeping their own server's IP out of some target's access logs. Axios's official request config exposes a built-in proxy option with host, port, protocol, and auth fields — it's been there for years, and it's the first thing every tutorial (including this one) shows you.
Here's the part that gets glossed over: that proxy option behaves differently depending on whether you're hitting an HTTP or HTTPS target, and depending on which Axios version you're running. That distinction is the entire reason this guide exists.
Setting Up Node.js and Axios (Quick Baseline)
Skip this if you've already got a project going. If not, it takes about two minutes.
mkdir axios-proxy-demo && cd axios-proxy-demo
npm init -y
npm install axios
Add "type": "module" to your package.json if you want ESM imports (I do — CommonJS require() for a proxy demo feels dated). Current Node LTS is v24.18.0, though I ran my tests on v22.22.3 specifically so the results wouldn't depend on the newest runtime quirks.
Drop this in app.js and run node app.js:
import axios from 'axios';
const res = await axios.get('https://httpbin.org/ip');
console.log(res.data);
You should see your real IP address in the response. That's your baseline — once a proxy is working, this same request should return the proxy's IP instead.
Record this response before enabling the proxy; it gives you a concrete baseline to compare with the proxied request in the next step.
Does Axios Actually Support HTTPS Proxies? (Setting the Record Straight)
Short answer: yes, in the current stable release. Axios 1.19.0 documents CONNECT tunneling for HTTPS targets behind an HTTP proxy. The npm downloads API recorded 117,890,039 Axios downloads from July 31 through August 6, 2026, a dated indicator of how widely the library is used. When you hit an HTTPS URL through a proxy, current Axios sends a CONNECT request to establish a tunnel, and your TLS handshake happens end-to-end with the real origin. I tested this directly: local HTTP proxy, local HTTPS origin with a self-signed cert, and the CONNECT counter on my proxy incremented exactly as expected.
So why does "Axios HTTPS proxy broken" show up in basically every forum thread about this? A few reasons, and they're all real:
- Old Axios versions. The GitHub issues people link to are often years old and describe release- and configuration-specific behavior that should not be generalized to current Axios.
- Proxy servers without CONNECT support. In that configuration, the tunnel fails and Axios should surface an error; capture the actual route and error before diagnosing an IP bypass.
- Confusing
proxyconfig with something it's not. Theproxyoption is a forward-proxy instruction, not a generic "route everything through this agent no matter what" switch.
Chromium reported in 2023 that more than 90% of Chrome navigations across major platforms used HTTPS. That is a dated Chrome measurement, not a current census of the whole web, but it explains why HTTPS-target behavior belongs at the center of this tutorial. If you're on an old Axios release, reproduce the problem on the current line before assuming a historical issue still describes current behavior; test the upgrade in your own application before deploying it.
When You Still Want an Explicit Agent
Native proxy config is fine for a single, static, conventional proxy. But it falls apart the moment you need per-request control, proxy rotation, or SOCKS support — Axios's built-in option just isn't built for that. That's where HttpsProxyAgent earns its keep, and I'll walk through it below. Think of the native option as "good enough for one proxy, one purpose" and the agent-based approach as "what you actually want in production."
The Node v24 and v22.21+ Environment Proxy Path
Recent Node releases ship a built-in environment-proxy mode, activated via NODE_USE_ENV_PROXY=1 or the --use-env-proxy flag. According to Node's own CLI docs, this landed in v24.0.0 and got backported to v22.21.0 — so "Node 22+" is technically wrong; it's specifically v22.21.0 and later within that line. If you're on an earlier Node 22 patch, this flag doesn't exist for you at all.
Current Axios already resolves HTTP_PROXY, HTTPS_PROXY, and NO_PROXY through its proxy-from-env dependency, so global-agent is not required for this current Axios path. When Node's own env-proxy mode is also active, two layers may be involved in the routing decision.
Axios's docs note that on Node versions where the agent carries a proxyEnv property, Axios defers to Node's handling instead of doing its own resolution. In practice, this means you should pick one system and stick with it:
- Letting Node handle it: set the flag, don't set Axios's
proxyconfig, and let the env vars do the work. - Letting Axios handle it: don't set the Node flag, and let Axios's own env-var resolution kick in.
- Taking full manual control: set
proxy: falseexplicitly and supply your ownhttpsAgent— this sidesteps both automatic systems entirely, which is what I recommend once you need rotation or per-request logic.
I tested the Axios-side resolution directly: setting HTTP_PROXY in a child process's environment routed the request through my local proxy, and adding a matching NO_PROXY entry made the next request skip it correctly. So the env-var path genuinely works out of the box now — it's the double-mode scenario you need to watch for.
5 Ways to Wire a Proxy Into Axios (Compared)
Before jumping into code, here's the lay of the land. I built and tested each of these against a real local proxy setup, not just from reading docs.
| Method | HTTPS Support | Auth Support | Per-Request Control | Rotation-Friendly | Complexity |
|---|---|---|---|---|---|
Inline proxy option | ✅ (current Axios) | ✅ | ✅ | ❌ | Low |
axios.create() defaults | ✅ (current Axios) | ✅ | ❌ (instance-wide) | ❌ | Low |
Env vars (HTTP_PROXY/HTTPS_PROXY) | ✅ | ✅ | ❌ | ❌ | Low |
httpsAgent + HttpsProxyAgent | ✅ | ✅ | ✅ | ⚠️ (manual) | Medium |
| Request interceptor + agent pool | âś… | âś… | âś… | âś… | Medium-High |
Use the inline option for a quick script hitting one proxy. Use axios.create() when every request in a module should go through the same proxy without repeating config. Use env vars when your infrastructure team already manages proxy routing centrally and you just want to inherit it. Reach for an explicit agent when you need control that native config can't give you — and reach for the interceptor pattern the moment "control" turns into "rotation."

Step-by-Step: Basic Proxy Configuration in Axios
The simplest setup uses the built-in proxy object directly on the request:
import axios from 'axios';
const res = await axios.get('https://httpbin.org/ip', {
proxy: {
host: '203.0.113.10',
port: 8080,
protocol: 'http',
},
});
console.log(res.data);
Run this and you should see the proxy's IP in the response instead of your own. If you're testing locally with a real proxy, this usually resolves in well under a second — compared to, say, manually configuring a system-wide proxy setting just to test one request, which is the kind of thing that eats fifteen minutes you don't have.
Compare this response with the baseline. A successful test should show the proxy's public IP rather than the origin IP you recorded earlier.
Using axios.create() for Instance-Wide Defaults
If every request in a given module should route through the same proxy, bake it into an instance instead of repeating the config:
const client = axios.create({
proxy: {
host: '203.0.113.10',
port: 8080,
},
timeout: 15_000,
});
const res = await client.get('https://httpbin.org/ip');
I verified that a proxy: false override on an individual request bypasses the instance default cleanly — useful if 95% of your calls need the proxy but a handful (say, a health-check ping) shouldn't.
Setting Proxy via Environment Variables
For centrally managed routing — think Docker containers or CI environments where ops already sets proxy env vars — you don't need to touch Axios config at all:
export HTTP_PROXY=http://203.0.113.10:8080
export HTTPS_PROXY=http://203.0.113.10:8080
export NO_PROXY=localhost,127.0.0.1
Current Axios reads these variables without global-agent. Just remember the Node version boundary above: if NODE_USE_ENV_PROXY is also active, make the routing owner explicit and test NO_PROXY behavior in the deployed runtime.
Step-by-Step: HTTPS Proxy Setup With httpsAgent (For Real Control)
This is the setup I'd actually recommend once you need more than "one proxy, forever." Install the current agent package:
npm install https-proxy-agent
https-proxy-agent 9.1.0 requires Node 20 or newer and issues a proper CONNECT to your proxy before tunneling the target connection through it.
import axios from 'axios';
import { HttpsProxyAgent } from 'https-proxy-agent';
const agent = new HttpsProxyAgent('http://203.0.113.10:8080');
const client = axios.create({
proxy: false, // stop Axios's native resolution from also chiming in
httpsAgent: agent,
timeout: 15_000,
});
const res = await client.get('https://httpbin.org/ip');
console.log(res.data);
Set proxy: false when an explicit agent owns routing. That makes the configuration unambiguous and prevents Axios's native or environment-proxy resolution from competing with the supplied agent.
Adding Proxy Authentication
Embed credentials right in the proxy URL:
const agent = new HttpsProxyAgent('http://myuser:mypassword@203.0.113.10:8080');
If your password has special characters — @, :, # are the usual troublemakers — percent-encode them before building the URL, or construct the string with encodeURIComponent() on each component. A raw @ in a password will get parsed as the start of the host section, and you'll get a connection error that has nothing obviously to do with encoding.
Using SOCKS5 Proxies With Axios
SOCKS proxies aren't compatible with HttpsProxyAgent — you need a different agent for the protocol:
npm install socks-proxy-agent
import { SocksProxyAgent } from 'socks-proxy-agent';
const agent = new SocksProxyAgent('socks5://myuser:mypass@203.0.113.10:1080');
const client = axios.create({
proxy: false,
httpsAgent: agent,
});
socks-proxy-agent 10.1.0 also needs Node 20+. SOCKS5 is worth reaching for when you're dealing with corporate networks that only expose a SOCKS gateway, or with proxy providers that offer more flexible protocol support than plain HTTP proxies allow.
Rotating Proxies With Axios Request Interceptors
Picking a random proxy inside your calling code works fine for a one-off script. It falls apart the moment you're making hundreds of requests, because there's no central place tracking which proxies are dead, no retry logic, and the proxy-selection code ends up copy-pasted everywhere. Axios's interceptor system gives that logic one testable home; none of the five competitor tutorials in this article's SERP review used this pattern.

Building a Proxy Pool
import axios, { AxiosError, InternalAxiosRequestConfig } from 'axios';
import { HttpsProxyAgent } from 'https-proxy-agent';
class ProxyPool {
private agents: HttpsProxyAgent<string>[];
private index = 0;
constructor(proxyUrls: string[]) {
this.agents = proxyUrls.map((url) => new HttpsProxyAgent(url));
}
next(): HttpsProxyAgent<string> {
const agent = this.agents[this.index];
this.index = (this.index + 1) % this.agents.length;
return agent;
}
}
const pool = new ProxyPool([
'http://user:pass@proxy1.example.com:8080',
'http://user:pass@proxy2.example.com:8080',
]);
const client = axios.create({ timeout: 15_000 });
client.interceptors.request.use((config: InternalAxiosRequestConfig) => {
config.proxy = false;
config.httpsAgent = pool.next();
return config;
});
I ran this against two local proxies and confirmed the requests alternated correctly — proxy A, then proxy B, then back to A. Note that Axios executes request interceptors last-in-first-out, so if you've got other interceptors (auth headers, logging), order matters more than you'd think.
Adding a Response Interceptor With Retry Guards
This is where most DIY rotation scripts get sloppy. Retrying blindly on every failure, against an unlimited pool, can turn one bad request into a cascading mess — especially for non-idempotent methods like POST, where retrying might duplicate a side effect you really didn't want duplicated.
type RetryableConfig = InternalAxiosRequestConfig & {
__proxyRetryCount?: number;
};
client.interceptors.response.use(
undefined,
async (error: AxiosError) => {
const config = error.config as RetryableConfig | undefined;
if (!config) throw error;
const method = String(config.method ?? 'get').toUpperCase();
config.__proxyRetryCount ??= 0;
if (method !== 'GET' || config.__proxyRetryCount >= 1) throw error;
config.__proxyRetryCount += 1;
config.proxy = false;
config.httpsAgent = pool.next();
return client.request(config);
}
);
I tested this against a deliberately broken proxy and confirmed exactly one retry fired on the alternate agent — no infinite loop, no retry on a POST request. That's the bound you want: a retry policy that's honest about which requests are safe to replay, not a "just try again until it works" hack.

Error Diagnostic Table: Map Every Failure to Its Fix
Bookmark this. These are the errors that actually show up in Axios GitHub issues and Stack Overflow threads, not hypothetical ones.
| Error / Symptom | Likely Cause | Fix |
|---|---|---|
ECONNREFUSED | Wrong host/port, or proxy server is down | Verify with curl -x http://host:port target-url before touching Axios code |
407 Proxy Authentication Required | Missing or wrong credentials | Add auth: { username, password } to the proxy config, or embed creds in the HttpsProxyAgent URL |
403 Forbidden | The origin or WAF rejected the request or proxy IP | Check the site's access policy, authentication, and request rate; do not treat a different header or IP as permission to bypass restrictions |
| Response shows your real IP | NO_PROXY, proxy:false, an explicit direct agent, or a historical/version-specific configuration may be bypassing the proxy | Inspect which layer owns routing; verify the path with a controlled IP endpoint and explicit agent if needed |
ETIMEDOUT | Connect or response interval exceeded the configured timeout | Measure where time is spent; adjust the timeout only if the workload justifies it, otherwise replace or cool down the unhealthy route |
ECONNRESET mid-response | The proxy, network, or origin closed the connection | Record the failing hop; retry only replay-safe requests with a finite budget |
502 Bad Gateway behind Nginx | Nginx's proxy_pass misconfigured, or Axios's timeout doesn't match Nginx's | Check proxy_connect_timeout and proxy_read_timeout (both default to 60s) and align them with your Axios timeout |
ERR_TLS_CERT_ALTNAME_INVALID | Wrong agent type for the target, or a self-signed cert | Confirm you're using the right agent for the protocol; set rejectUnauthorized: false for local testing only — never in production |
Quick Debugging Checklist
When something breaks and you can't tell why, work through this in order:
- Test the proxy directly with
curl -x http://host:port https://your-target.com. If it fails, investigate proxy connectivity, authentication, and the target before changing Axios. If it passes, the Axios path still needs separate verification. - Confirm which Axios and Node versions you're actually running, and compare historical reports with the same release/configuration before applying their fixes.
- Figure out which system is resolving the proxy — native Axios config, Axios's env-var resolution, Node's built-in env-proxy mode, or an explicit agent. Never let more than one own the same request.
- Check
NO_PROXYfor accidental hostname matches. - If you're using an explicit agent, confirm
proxy: falseis set so Axios doesn't try to double-handle it.
When to Skip DIY Proxy Plumbing Entirely
All of the above is genuinely useful if your actual goal is routing arbitrary traffic — corporate network testing, geo-testing an app, or controlled network egress. But many developers land on "how do I set up an Axios proxy" because what they really want is data from a website, and the proxy is just a means to that end.
If that's your situation, it's worth asking whether you need a proxy at all, or whether you need a scraping API that handles the infrastructure for you. Thunderbit's Open API takes a URL and a schema and returns structured JSON — no raw HTML parsing, no agent libraries, no proxy pool to babysit. The /extract endpoint handles JS-rendered pages, anti-bot measures, and CAPTCHAs server-side, and there's a lighter /distill endpoint that just converts a page to clean Markdown if that's all you need. There's also an MCP server exposing tools like thunderbit_extract and thunderbit_suggest_fields, so coding assistants like Claude or Cursor can pull structured data mid-task without touching a proxy config at all, plus a CLI for terminal and CI workflows.
| Concern | DIY Axios + Proxies | Thunderbit API/MCP/CLI |
|---|---|---|
| Proxy sourcing & rotation | You manage | Handled server-side |
| Browser and access challenges | You operate the browser/network layer | Managed by the service within its documented capabilities |
| JS-rendered pages | Need a headless browser | renderMode: full |
| Output format | Raw HTML → you parse | Structured JSON via schema |
| Maintenance when sites change | You maintain parsing/selectors | The managed extraction layer reduces some application-side maintenance |
The honest framing: if you need to route traffic for testing or corporate networking, none of this replaces Axios and a proxy config. If your deliverable is structured web data, an API-first approach may reduce the proxy, browser, and parsing code your application owns. At retrieval on August 7, 2026, Thunderbit's API rate-limit documentation listed its Free tier at 10 requests per minute and 2 concurrent requests. Treat those as time-sensitive API limits and recheck the page before relying on them in production.
Wrapping Up
The core lesson here cuts against what many older tutorials tell you: current Axios documents and, in the recorded local test, correctly used CONNECT tunneling for an HTTPS target. Historical failures still matter, but they need version and configuration context. Native config is a good low-complexity starting point; when you need per-request control, SOCKS support, or rotation, an explicit HttpsProxyAgent (or SocksProxyAgent) paired with proxy: false gives you clearer ownership. And if you're rotating across a pool in production, request and response interceptors provide a centralized, testable place to do it — just make sure your retry logic has a loop guard and only replays requests that are actually safe to replay.
Bookmark the diagnostic table above for the next time a proxy setup throws a cryptic error at 2am. And if you catch yourself spending more time debugging proxy plumbing than actually using the data you're trying to get, it might be worth checking whether an API-first extraction tool solves the actual problem faster than the infrastructure ever will.
FAQs
Does Axios support HTTPS proxies natively? Yes in current Axios for a conventional HTTP proxy: the current documentation describes CONNECT tunneling for HTTPS targets, and Axios 1.19.0 passed that route in the recorded local test. Historical releases and particular proxy configurations produced real failures, so verify the exact version and proxy rather than assuming either universal success or universal failure.
How do I rotate proxies in Axios?
Use a request interceptor to assign a different httpsAgent from a proxy pool before each request goes out, and pair it with a response interceptor that retries failed requests on a different proxy. Keep the retry logic bounded — one retry, and only for idempotent methods like GET — so you don't accidentally replay a request that shouldn't be replayed.
Why does my Axios proxy show my real IP?
Check whether NO_PROXY, proxy:false, an explicit direct agent, or deployment-specific routing bypassed the proxy. Record the Axios/Node versions and test the proxy independently with cURL. If you need unambiguous per-request routing, use HttpsProxyAgent with proxy:false and verify the observed IP on a controlled endpoint.
Can I use SOCKS5 proxies with Axios?
Yes, through the socks-proxy-agent package. Create a SocksProxyAgent instance with your SOCKS URL and pass it as httpsAgent in your Axios config — just make sure you're not also passing HttpsProxyAgent, since the two protocols use different agent types.
What's the difference between the proxy option and httpsAgent in Axios?
The proxy option is Axios's built-in config for a single, static proxy and works fine for straightforward use cases in current versions. httpsAgent accepts a custom Node.js agent — like HttpsProxyAgent or SocksProxyAgent — giving you direct, per-request control over routing, authentication, and rotation that the native option was never designed to handle.


