7 min read

Proxy environment variables: audit agent API traffic

Audit proxy environment variables before AI agents call internal APIs. Verify inheritance, test NO_PROXY matching, and contain unsafe proxy routes.

Proxy environment variables: audit agent API traffic

Proxy environment variables are executable routing instructions, not harmless shell preferences. If an AI coding agent inherits HTTP_PROXY, HTTPS_PROXY, ALL_PROXY, or NO_PROXY, a request that looked like a direct call to an internal API may take a different network path before the agent has done any useful work.

I have seen teams spend days reviewing token scope and endpoint allowlists, then discover that the agent runner inherited a local debugging proxy from a developer shell. The credentials were valid, the API client behaved exactly as configured, and the traffic still went somewhere nobody intended. Audit the process environment before you grant an agent access to internal services.

Proxy variables change the route, not just the connection settings

A proxy variable tells a cooperating client to hand its request to an intermediary. For ordinary HTTP, the client commonly sends the full destination URL to that intermediary. For HTTPS, the client usually asks the proxy to open a CONNECT tunnel to the target host, then performs TLS through that tunnel.

That distinction matters because a proxy can affect availability, destination control, DNS behavior, and observability even when it cannot decrypt HTTPS. A proxy can refuse a connection, redirect it at the TCP layer, record the requested host and port, or become the only path an agent can use to reach a service.

Treat these variables as part of the agent's outbound authority:

  • HTTP_PROXY and http_proxy commonly affect http:// URLs.
  • HTTPS_PROXY and https_proxy commonly affect https:// URLs.
  • ALL_PROXY and all_proxy act as a fallback in clients that support them.
  • NO_PROXY and no_proxy commonly exempt destinations from proxying.

The word "commonly" carries weight. Environment variables are a convention, not a network standard that every runtime implements identically. An agent may call a command-line client, use a language HTTP library, invoke a package manager, or start a helper process. Each layer may make a separate proxy decision.

An unset variable does not prove a direct route either. A client can read a config file, use a system proxy setting, obey a PAC file, or call a local relay explicitly. This article focuses on environment variables because they are easy to inherit, hard to notice in a crowded process environment, and often treated as temporary even after they have become permanent.

The launcher decides what the agent inherits

An agent only receives variables that exist in its own process environment or that a parent passes down. The terminal where you checked env may have nothing to do with the process that actually runs the agent.

On macOS, this is especially easy to get wrong. A process launched from an interactive shell inherits the shell's exported variables. A graphical app launched by Finder or a service launched through launchd follows a different inheritance path. An IDE may start its integrated terminal with one environment and its extension host with another. A background agent started yesterday can keep an old proxy value long after the shell variable disappeared.

Map the execution chain before changing settings. Ask four concrete questions:

  1. What process starts the agent?
  2. Does the agent start shells, package tools, test runners, or remote helpers?
  3. Which of those processes make HTTP calls?
  4. Which launch point supplies each environment variable?

Do not accept "the agent runs in my terminal" as an answer unless you can identify the parent process and reproduce the run from that terminal. The agent may be a child of an editor, a task runner, or an automation service that uses a saved environment.

A proxy injected by a parent process reaches every child unless a child removes it. That is why a one-line shell export can quietly alter calls made by package installation, source control helpers, cloud CLIs, browser automation, and test fixtures. The affected request may not be the one you had in mind when you started the agent.

Proxy variable semantics differ across clients

There is no single interpretation of HTTP_PROXY, HTTPS_PROXY, and NO_PROXY. Any security review that assumes one client’s behavior applies to another is incomplete.

The curl documentation makes an important exception that many people miss: curl accepts only lowercase http_proxy for HTTP proxy configuration. Its documentation explains that this avoids a CGI problem, where an incoming Proxy: header can become an HTTP_PROXY environment variable. curl accepts uppercase variants for several other proxy variables, but this HTTP exception is intentional.

Go documents http.ProxyFromEnvironment as a function that reads HTTP_PROXY, HTTPS_PROXY, and NO_PROXY, with lowercase alternatives. Its behavior also includes CGI protection: when a CGI environment contains REQUEST_METHOD, Go refuses to use HTTP_PROXY because a request header may have supplied it. That protection does not mean a Go process is safe. HTTPS_PROXY, lowercase forms, explicit transport settings, and non-CGI contexts still need review.

Many JavaScript applications complicate the picture. The Node.js runtime's built-in HTTP APIs have historically not imposed one universal environment proxy policy. Applications and their dependencies often add proxy support themselves. One command may honor HTTPS_PROXY; another command in the same agent run may ignore it; a third may read a custom option instead.

Do not settle this with a spreadsheet of runtime folklore. Identify each network-capable executable and test it. Record the executable version, its invocation, the target URL, the relevant variables, and whether it connected directly or through the intended proxy. Keep the result with the agent's deployment notes, because a dependency update can change behavior.

A useful distinction is often blurred: proxy awareness is not proxy enforcement. A client that honors a proxy variable can be routed when the variable exists. It can still connect directly when the variable is absent, malformed, bypassed by NO_PROXY, or ignored by a helper process. If you need to prevent direct egress, enforce that at the network boundary, not by hoping every library reads an environment variable.

NO_PROXY needs explicit tests for internal names

NO_PROXY is a bypass list, and a bad bypass list can send internal traffic through a proxy or send traffic direct when you expected inspection. Neither outcome is safe to assume away.

Most implementations accept comma-separated entries. Beyond that, details diverge. Clients may treat registry.corp.example, .corp.example, corp.example, 10.20.0.0/16, 10.20.30.40, localhost, and * differently. A suffix match that works in one client may be too broad or fail completely in another. Port-qualified entries also do not behave uniformly.

Avoid broad entries until a test proves their meaning. A bare domain suffix can exempt hosts you did not intend. An asterisk can disable proxying much more widely than a reviewer expects. CIDR support is useful where it exists, but it is not a portable assumption. Exact internal hostnames are boring, and boring is useful here.

Start with a short list of named services that must connect directly, such as an internal source host, artifact registry, and service discovery endpoint. Add a domain suffix only after you have established that the client matches it as intended and that every host beneath the suffix deserves the same route.

Also separate hostname matching from name resolution. A client often decides whether NO_PROXY applies before it connects. If your list contains a hostname, the matching may succeed even if the host resolves to an address outside your expected range. If your list contains an IP range, the client may need to resolve the name before it can decide. The implementation controls this sequence.

A bypass test needs a destination that you control and can identify in logs. Do not use a production API and infer success from a 200 response. An internal test endpoint should report the remote address or emit a request identifier in its access log. Then compare a request made with the bypass entry present against one made without it. You want evidence of the connection path, not a guess based on application output.

HTTPS hides content, but a proxy still gets meaningful data

Give agents an action gateway
Use the bundled sp mcp shim to let MCP-capable agents request HTTP and SSH actions.

An HTTPS CONNECT tunnel usually protects request headers and bodies from a conventional forward proxy. It does not make the proxy irrelevant. The proxy sees the host and port named in the CONNECT request, connection timing, byte counts, and often the source address. Depending on the client and network, related DNS traffic may reveal more.

The proxy can read decrypted API credentials if the client trusts a certificate authority that the proxy uses for TLS interception. This happens in some managed corporate networks and debugging setups. The presence of a trusted local root certificate is a security boundary decision, not a convenience setting. If an agent process trusts that root, a proxy operator can inspect traffic to any host for which the interception policy applies.

Plain HTTP is worse. A forward proxy can receive the full URL and request headers, including bearer tokens or basic authentication. Do not allow an agent to use plaintext HTTP for authenticated internal APIs because "the network is private." Proxy environment variables are one of several ways private-network assumptions become false.

A less obvious failure involves a proxy URL with embedded credentials, such as http://user:[email protected]:8080. That value may show up in diagnostic output, crash reports, shell history, process inspection, or logs that record the environment. Proxy authentication should use a managed mechanism appropriate to your environment, and a security review should treat proxy credentials as secrets in their own right.

If you cannot identify the proxy operator, the proxy's listener address, and whether TLS interception is possible, do not route privileged agent traffic through it. This is not paranoia. The proxy has already become part of the path between an automated process and a sensitive service.

Audit the running process without dumping its secrets

Begin with an inventory that reports variable names and proxy endpoints, while avoiding a broad environment dump. In a shell that might launch the agent, run:

env | grep -Ei '(^|_)(http|https|all|no)_proxy='

The output shape should look like this:

HTTPS_PROXY=http://127.0.0.1:8888
NO_PROXY=localhost,127.0.0.1,registry.corp.example

If the proxy URL contains user information, do not paste the raw output into a ticket. Record the scheme, host, and port after removing credentials. A loopback address is not automatically safe. Local proxies often belong to legitimate debugging tools, but malware and unwanted software can listen on loopback too. Verify which process owns the listening port.

On macOS, inspect a listener with:

lsof -nP -iTCP:8888 -sTCP:LISTEN

A normal result identifies a command and process ID. If no expected process owns the port, stop there and investigate. Do not let an agent route credentials to a listener just because the address starts with 127.0.0.1.

Next, inspect the actual agent process. ps can display a process environment on macOS, but it can expose unrelated secrets. Limit access to the machine owner or an administrator, collect only what you need, and do not paste the result into chat or a shared log.

ps eww -p "$AGENT_PID" | tr ' ' '\n' | grep -Ei '(^|_)(http|https|all|no)_proxy='

Set AGENT_PID to the running agent's process ID. This command can still print sensitive proxy credentials if they exist, so use it on a trusted terminal and redact before saving evidence. If the output differs from your shell inventory, the parent process injected or removed variables.

For processes started by launchd, examine the job definition and the launcher rather than relying on your terminal. launchctl getenv HTTPS_PROXY can reveal a variable in the current launchd domain, but absence there does not clear a particular job. A job can define its own environment, and a wrapper script can export variables just before it starts the agent.

Prove the route with a harmless request

Avoid credential-filled environments
Route agent API work through one signed macOS app, rather than handing credentials to every helper process.

A configuration review tells you what should happen. A controlled request tells you what happened. You need both.

Create or use a non-sensitive endpoint that logs the peer address and request path. Then make a request with verbose connection output. curl is useful because it prints whether it connects to the proxy and whether it sends a CONNECT request.

HTTPS_PROXY=http://127.0.0.1:8888 \
NO_PROXY= \
curl -v --connect-timeout 5 https://probe.corp.example/agent-proxy-check

When curl uses the proxy for HTTPS, its verbose output typically includes lines with this shape:

* Uses proxy env variable HTTPS_PROXY == 'http://127.0.0.1:8888'
* Establish HTTP proxy tunnel to probe.corp.example:443
> CONNECT probe.corp.example:443 HTTP/1.1

Then run the direct comparison deliberately:

HTTPS_PROXY=http://127.0.0.1:8888 \
NO_PROXY=probe.corp.example \
curl -v --connect-timeout 5 https://probe.corp.example/agent-proxy-check

A direct run should show a connection to the target rather than a CONNECT request to the proxy. Confirm that result in the probe's server logs. If curl says it bypassed the proxy but the server sees an unexpected source or the request fails, inspect DNS, routing, and any transparent network proxy separately.

This proves curl, not your agent. Repeat the test through the agent's exact execution route. If it invokes a script, run that script. If it uses a dependency to call an API, add a temporary probe URL through that same configuration. If it launches a helper process, collect the helper's route evidence. A test of a different client is only a clue.

Do not use a public "what is my IP" service for this job. That converts an internal routing review into an unnecessary external disclosure, and it cannot tell you which internal proxy policy applied.

Clean launch environments beat permanent shell exports

Do not put corporate proxy exports in a universal shell profile and then expect autonomous tools to make safe exceptions. Global exports are popular because they make a blocked command work once. They also spread to every child process you later forget exists.

Use an explicit launcher for the agent. Start from a known environment, pass only the variables the run needs, and make proxy use visible in the launch command or wrapper. On a Unix shell, env -i clears inherited variables, so you must restore the basics required by the program:

env -i \
PATH="$PATH" \
HOME="$HOME" \
LANG="${LANG:-en_US.UTF-8}" \
NO_PROXY="localhost,127.0.0.1,registry.corp.example" \
agent-command

This example intentionally does not set a proxy. If the agent needs one, add the proxy variable in the launcher after you have identified its owner and tested its behavior. Do not copy a proxy value from a shell profile into a script without checking whether it contains credentials or points at a stale local service.

A clean environment can break tools that relied on variables such as certificate locations, cloud profiles, SSH agent sockets, or package caches. That breakage is useful information. Add back only the variables the agent needs, one by one, and document why each is present. An agent launcher should be narrow enough that another engineer can read it and understand where its requests will go.

Network controls should back this up. If an agent must only reach a few internal services, use firewall rules, an egress proxy with enforced policy, or a dedicated network segment appropriate to your environment. Environment variables choose a route for cooperative clients. They do not prevent a compromised process or a noncompliant library from opening a direct socket.

Keep credentials out of the process that chooses the route

Verify the action history
Verify Sallyport's hash-chained audit trail offline over ciphertext with no vault key required.

Proxy hygiene reduces accidental rerouting, but it does not make it wise to hand API tokens to an autonomous agent. If the agent environment contains a bearer token, every process with access to that environment becomes part of the secret's exposure path.

Sallyport takes a different boundary: the agent asks for an HTTP or SSH action through its MCP shim, while credentials remain in the app's encrypted vault and the app executes the action. That limits what a proxy-variable leak in the agent process can expose, because the agent never receives the API or SSH credential in plaintext.

Do not overstate that boundary. A credential gateway does not magically define safe proxy behavior for every command the agent runs, and it cannot make a harmful destination harmless. You still need to control which destinations and actions you permit, review the application process that performs the network call, and preserve evidence of the request path.

The useful separation is between secret custody and network routing. Secret custody answers who can read the credential. Network routing answers which intermediary handles the request. Teams often solve one and assume they solved both. They have not.

Treat unexplained proxy use as a contained incident

If a privileged agent made requests through an unknown proxy, stop the run before investigating from inside the same contaminated environment. Record the agent process ID, its parent process, the proxy address, the affected destination names, and the time window. Preserve relevant agent and proxy logs under your incident process.

Then remove the variable at its source. Deleting it in your current terminal fixes only future children of that terminal. Check shell profiles, IDE task settings, launch agents, CI configuration, wrapper scripts, and any configuration management that writes environment settings. Restart the affected process after correcting the launch point.

Assess credentials by protocol. If the affected traffic was plaintext HTTP with authentication, rotate the exposed credential. If it was HTTPS through a proxy, determine whether the endpoint validated normal TLS and whether the agent trusted an interception certificate. Do not assume that HTTPS means the event needs no review, and do not rotate blindly while leaving the same injection path in place.

Finally, add a preflight check where the agent starts. Fail the run or require an explicit review when an unexpected proxy variable appears. The check should report the variable name and sanitized endpoint, compare it against the approved route for that job, and leave a record that the preflight ran. The first unexplained proxy is a warning. The second is a deployment defect you chose to keep.

FAQ

What do HTTP_PROXY and HTTPS_PROXY do?

They tell compatible HTTP clients to send requests through a proxy rather than opening a direct connection to the destination. The variables do not force every program to comply, so you must test the actual agent, its tools, and its subprocesses.

Can an HTTPS proxy read API tokens?

Sometimes. A proxy handling an HTTPS CONNECT tunnel normally sees the destination hostname and port but not the encrypted request body. It can read HTTPS traffic only if the client trusts a certificate authority that lets the proxy intercept TLS, or if the client sends sensitive material before TLS starts.

What is NO_PROXY used for?

NO_PROXY is a bypass list. It tells many clients to connect directly to listed hosts, domains, or IP addresses, but the exact matching rules vary by client library and version.

Does NO_PROXY support wildcard domains?

Do not assume that a leading dot, a bare suffix, a CIDR block, or an asterisk means the same thing everywhere. Run direct tests with the exact command or library your agent uses, then keep the list small and explicit.

Why are proxy variables risky for AI agents?

It can be. If an agent process inherits a proxy address from a shell, IDE, CI runner, or service manager, its requests may leave through that route without any prompt. The risk rises when the proxy belongs to a VPN helper, debugging tool, hotel network setup, or an unknown local listener.

How do I audit proxy settings on macOS?

Start with a redacted inventory: env | grep -Ei '(^|_)(http|https|all|no)_proxy='. Then inspect the launcher and process environment, identify the proxy owner and listening address, and prove the route with a harmless request before allowing access to internal services.

Do all HTTP clients honor proxy environment variables?

No. curl, Go, Python, Java, Node packages, and command-line tools make their own decisions about environment variables. Some honor uppercase and lowercase names, some have CGI safeguards, and some need separate proxy configuration.

Does NO_PROXY prevent DNS leaks?

A direct connection can still leak a hostname through DNS if the resolver is outside the network boundary you expect. It can also fail because the host has no direct route, which is why a direct test must check both the TCP path and the application response.

What should I do if an agent used an unknown proxy?

Treat it as an incident when the route changes for sensitive traffic or an unknown proxy receives requests. Stop the agent, remove the inherited variables at the actual launch point, revoke any credentials that might have crossed an untrusted HTTP route, and preserve process and proxy logs.

Does a credential gateway eliminate proxy configuration risks?

No. A gateway can keep API and SSH credentials out of the agent process, which limits credential exposure, but it does not automatically decide how every unrelated client resolves proxy variables. You still need a clean launch environment and a tested network path.

Sallyport

Sallyport runs API calls and SSH commands for your AI agent. The keys stay in a local vault on your Mac; you approve each run and every action lands in a sealed journal.

© 2026 Sallyport · Open source under Apache-2.0 · Oleg Sotnikov