# Proxy Configured, Traffic Direct: 1,080 Tests of Four Common HTTP Clients *Original FreePolicyTools research · 16 October 2026* A proxy environment variable can be present while an application connects directly to its destination. In our tests, the difference depended on the client, the spelling of the setting, the URL scheme, and whether proxy support had been enabled. We ran **1,080 controlled local trials**, followed by **80 public HTTPS attempts**, using curl, Python’s urllib, Python Requests, and Node’s native fetch. The local experiment covered 360 combinations repeated three times. Node was tested with its default configuration and with environment proxy support explicitly enabled. The failures themselves produced a reassuring result within this test: among **420 failure trials matched to healthy baselines that used the proxy for every request leg, none delivered a direct request to our destination**. Direct traffic appeared in configurations where the proxy was bypassed or never engaged. These are application routing findings. We did not test a paid proxy provider, discover a new vulnerability, or measure browser anonymity. For background, read our [guide to proxy servers](/blog/what-is-a-proxy-server/) and the [difference between a VPN and a proxy](/blog/vpn-vs-proxy/). ## What we measured We configured a small proxy and two destination servers on one Debian PC. One destination served HTTP; the other served HTTPS. A third path redirected from the HTTP destination to the HTTPS destination. The destination recorded whether each synthetic request arrived from the proxy’s outgoing connection or directly from the client. For HTTPS, the proxy forwarded an encrypted CONNECT tunnel. It did not decrypt TLS. A temporary certificate was trusted only by the test processes, and a negative control confirmed that an unrelated certificate authority was rejected. Each trial launched a fresh client process with a controlled environment. We removed inherited proxy settings, disabled curl’s user configuration file, and used a temporary home directory. This prevented an existing shell setting or user configuration from silently changing the experiment. The installed versions were curl 8.14.1, Python 3.13.5, Requests 2.32.3, and Node 25.2.1. Our evidence records curl’s Debian patch version and complete runtime details. We tested four configurations: - Lowercase `http_proxy` and `https_proxy` together. - Uppercase `HTTP_PROXY` and `HTTPS_PROXY` together. - Lowercase `http_proxy` alone. - Both lowercase settings plus a deliberate `NO_PROXY` exception for the destination. Each configuration faced a healthy proxy and five failure conditions: connection refusal, HTTP 407 authentication required, HTTP 503 unavailable, a TCP reset, and a connection that accepted the request but stalled. ## Finding 1: default Node fetch did not engage the environment proxy With both lowercase proxy variables present, default Node fetch connected directly for HTTP, HTTPS, and the redirect path. Enabling `NODE_USE_ENV_PROXY=1` changed all three paths to use the proxy. We reproduced that distinction against a public HTTPS destination. Default fetch returned a valid response without contacting our researcher-operated proxy. The enabled mode established a CONNECT tunnel through it. This behavior follows the tested runtime’s configuration model. Node documents the environment proxy option; the result should not be described as a Node security vulnerability. It demonstrates why checking that an environment variable exists is insufficient evidence that a particular application uses it. [Node 25.2.1 documentation](https://nodejs.org/download/release/v25.2.1/docs/api/cli.html#node_use_env_proxy1) ## Finding 2: uppercase settings did not mean the same thing to every client In the local HTTP tests, curl connected directly when given uppercase `HTTP_PROXY`, while urllib, Requests, and proxy-enabled Node used the proxy. For HTTPS, curl used uppercase `HTTPS_PROXY`. curl deliberately treats `http_proxy` differently: it accepts the HTTP variable in lowercase to avoid a CGI-related security problem. The measured behavior is documented and intentional. [curl’s proxy environment documentation](https://everything.curl.dev/usingcurl/proxies/env.html) That produced a distinctive local redirect route. With uppercase settings, curl sent the initial HTTP request directly and the redirected HTTPS request through the proxy. A test that checked only the final HTTPS response would miss the first leg. We did not repeat this HTTP redirect scenario against a public destination, so its measured scope remains the controlled local fixture. ## Finding 3: an HTTP-to-HTTPS upgrade could change the route When only lowercase `http_proxy` was supplied, curl, urllib, and Requests used the proxy for the initial local HTTP request, then connected directly for the redirected HTTPS request. Proxy-enabled Node behaved differently: it used the HTTP proxy setting for both HTTP and HTTPS in this configuration. Default Node fetch connected directly throughout. | Client configuration | HTTP | HTTPS | HTTP → HTTPS | |---|---|---|---| | curl, `http_proxy` only | Proxy | Direct | Proxy → Direct | | urllib, `http_proxy` only | Proxy | Direct | Proxy → Direct | | Requests, `http_proxy` only | Proxy | Direct | Proxy → Direct | | Node fetch, default, `http_proxy` only | Direct | Direct | Direct → Direct | | Node fetch, proxy mode enabled, `http_proxy` only | Proxy | Proxy | Proxy → Proxy | All three repetitions agreed for each local combination. Public HTTPS checks reproduced the HTTPS column: curl, urllib, Requests, and default Node fetched directly; enabled Node used the test proxy. The practical implication is specific: validating one HTTP request does not establish how the same configuration routes HTTPS. Requests and urllib each document their proxy configuration mechanisms; users need to apply the configuration supported by the client they actually run. [Requests documentation](https://requests.readthedocs.io/en/stable/user/advanced/#proxies), [Python urllib documentation](https://docs.python.org/3.13/library/urllib.request.html) ## Finding 4: an engaged proxy did not fall back to direct traffic in our failure tests We first identified the client/configuration/path combinations that used the proxy for every leg in all three healthy baselines. There were 28 such combinations. Across their five failure conditions and three repetitions, we observed 420 matched failure trials. None reached the controlled destination by any route. A proxy-generated HTTP 407 or 503 response did not count as destination receipt. Some clients can return such a response to an application without a transport-level error, so an exit code alone would be an inadequate routing test. This result is bounded. It applies to these versions, settings, fixtures, and failure conditions. Our [HTTP versus SOCKS proxy guide](/blog/http-vs-socks-proxies/) explains why a different protocol requires its own testing. We did not test PAC fallback rules, browser extensions, SOCKS, mid-transfer failures, connection reuse, or every possible retry policy. ## What the public checks add Local addresses can receive special treatment from proxy implementations. We therefore repeated the HTTPS configuration comparisons using `api.ipify.org`, a public IP-echo endpoint. The public phase covered five client modes, four configurations, two proxy states, and two repetitions: 80 attempts. There were 62 valid responses—44 direct and 18 through the researcher proxy. The remaining 18 attempts returned no valid response in refused-proxy configurations whose healthy counterparts had used the proxy. These totals describe deliberately selected configurations. **They are not a population estimate that a percentage of proxy users leaks traffic.** Both repetitions agreed on the observed route or failed-response outcome for each public configuration. Our temporary forwarding proxy ran on the same PC as the clients. It therefore shared their Internet egress. Its CONNECT records establish that a request used the configured proxy path; they do not demonstrate that a remote paid proxy concealed the PC’s public IP. Raw public IP responses were handled in memory and saved only as opaque, salted comparison labels. ## A better way to check a proxy configuration For the application and version you use, establish a working baseline first. Confirm the proxy receives the request. Test each relevant URL scheme and redirect leg. Then make the proxy unavailable and check whether the destination still receives a request. Separate three outcomes: a client that never engaged the proxy, an intentional bypass rule, and a client that changes from a proven proxy route to a direct route after failure. Combining those outcomes into one “leak rate” would hide the cause. Our study’s central finding is a configuration gap: a proxy setting can look complete while leaving a particular client or request scheme on a direct route. The reproducible route evidence makes that gap visible. ## Evidence, exclusions, and disclosure The accompanying package contains the scripts, exact main-run source snapshots, individual trial records, server events, aggregates, controls, and SHA-256 manifests. The 360-trial exploratory fixture pilot is excluded from the main totals. An earlier Honeygain supplier setup attempt is a separate experiment. It authenticated but received “Network Overused”; it did not establish customer relay traffic or consent-withdrawal behavior, and it contributes no observations to this routing matrix. Our research is about application routing, rather than the separate browser and network mechanisms described in our [WebRTC and IPv6 leak guide](/blog/ip-leaks-webrtc-and-ipv6/). No proxy vendor supplied a paid endpoint or sponsored this measurement session. No café customer traffic was captured. All services were temporary, no recurring task was created, and neither the router nor the PC’s VPN configuration was changed.