# Axios proxy routing lab

This is a synthetic diagnostic, tested on Node **24.20.0**, Axios **1.20.0**, macOS arm64 and OpenSSL 3.6.4 on 28 September 2026. It uses only loopback servers, random ports and invented credentials. It measures route selection and failure behavior, not a proxy provider's availability, anonymity, speed or location.

## Reproduce

Download `package.json`, `package-lock.json`, `lab.mjs` and `proxy-check.mjs` into one new directory. Use Node 24.20.0 and an OpenSSL executable supporting `req -addext` (OpenSSL 1.1.1+). The lockfile pins Axios and its dependencies. `npm ci` downloads dependencies; the lab itself makes no external requests.

```sh
node --version
npm ci --ignore-scripts --no-audit --no-fund
env -u NODE_USE_ENV_PROXY -u NODE_OPTIONS node lab.mjs my-results.json
```

Expected: exit 0, `passed: 15`, `total: 15`, and four passing `recipeChecks`. The fixture asserts JSON response bodies, proxy GET/CONNECT observations, origin counts, authentication boundaries, expected errors and completion within 3 seconds per route case. It creates and deletes a temporary certificate/key; TLS verification stays enabled. Do not use its certificate or invented credentials in production.

The script clears inherited HTTP(S)/ALL/NO_PROXY and npm proxy settings within its process, then sets each case's environment explicitly. Native proxy startup flags must be unset because Node initializes those before the script runs. Tests run serially and must not be imported into a server.

## Observed routing

| Case | Expected result on this pinned environment |
|---|---|
| Direct HTTP | Origin receives one request; proxy receives none |
| Explicit proxy plus conflicting env and `NO_PROXY=*` | Explicit proxy wins |
| `HTTP_PROXY` only | HTTP request goes through proxy |
| Matching `NO_PROXY` | Environment-selected proxy is bypassed |
| `proxy: false` with ordinary agent | Environment-selected proxy is bypassed |
| Wrong `proxy.auth` | HTTP 407; origin receives no request |
| HTTPS explicit proxy and trusted origin CA | CONNECT, then verified TLS to origin |
| `HTTPS_PROXY` and trusted origin CA | CONNECT, then verified TLS to origin |
| HTTPS origin certificate not trusted | `DEPTH_ZERO_SELF_SIGNED_CERT`; no origin HTTP request |
| Plain custom `http.Agent` and `HTTP_PROXY` | Axios still resolves environment proxy |
| Custom Node agent with its own `proxyEnv` | Agent's proxy wins over conflicting process env |
| Same `proxyEnv` agent plus `proxy: false` | **Still proxied**: Axios does not turn off that agent's routing |
| Fetch adapter plus Axios `proxy` option | Direct in this process; Axios proxy option is ignored |
| Origin accepts request but never responds | `ECONNABORTED` with a 100 ms Axios timeout |
| `proxy: false` plus plain agent | Direct, even with `HTTP_PROXY` set |

The twelfth result qualifies the Axios documentation's broad statement about `proxy: false`: it controls Axios proxy resolution, but a proxy-capable custom agent can still proxy. To obtain the tested direct route, also select an ordinary agent without `proxyEnv`. Do not assume the same behavior for another adapter or an upgraded package.

For HTTPS, the proxy receives authentication on CONNECT. The origin did not receive `Proxy-Authorization`. The HTTP fixture strips that hop-specific header before forwarding; that observation is not a claim that every proxy strips it.

## Use the bounded check recipe

`proxy-check.mjs` exports the same `requestConfig` used by every route test. It selects the Node HTTP adapter, disables redirects, caps the response at 4096 bytes, sets a 5 second Axios timeout and adds a 6 second abort deadline. The CLI validates an HTTPS IP-echo response. It emits only success status/IP or failure status/code; it does not print Axios errors, request headers or credentials.

After your secret manager has populated `PROXY_HOST`, `PROXY_PORT`, `PROXY_USER` and `PROXY_PASSWORD`, run:

```sh
node proxy-check.mjs
```

Default target: `https://api.ipify.org?format=json`, a third-party IP echo API. `CHECK_URL` can select your own HTTPS endpoint returning `{"ip":"203.0.113.10"}` with its actual observed address. Set `PROXY_PROTOCOL=https` only when the connection to the proxy uses TLS; an HTTPS target does not by itself require that value. `PROXY_HOST` is a hostname, without a scheme or credentials. Do not put secrets directly in shell command history.

The exported helper was executed through the authenticated local proxy against a trusted synthetic HTTPS origin: valid IP body accepted, wrong 200 body rejected and incorrect proxy credentials rejected with 407. The helper accepts an optional `httpsAgent` for a custom trusted CA; the CLI uses the runtime's normal trust configuration. This publication did not test a paid proxy or make an external ipify request.

Useful failure/control commands requiring no real proxy:

```sh
# Expected config failure, exit 1, with sanitized JSON only.
env -u PROXY_HOST -u PROXY_PORT -u PROXY_USER -u PROXY_PASSWORD node proxy-check.mjs

# Expected assertion failure: the controlled fixture refuses native startup routing.
NODE_USE_ENV_PROXY=1 node lab.mjs
```

Do not change the HTTPS tests to `rejectUnauthorized: false` or `NODE_TLS_REJECT_UNAUTHORIZED=0`; that would remove the trust check being demonstrated. For a corporate CA, supply the verified CA through your approved trust configuration.

## Limits

HTTPS-to-proxy transport, SOCKS, redirects, browser routing, external providers and startup-enabled native fetch proxy routing are not tested. Error codes may differ across adapters/versions and timeout phases. Elapsed times in `results.json` only demonstrate bounded local completion and are not network benchmarks. A returned IP confirms what that echo endpoint observed, not geography, reputation or independence from your ordinary egress.
