Apus can route runtime HTTP and WebSocket traffic through an operator-managed forward proxy. This is optional defense in depth: central egress control, stronger SSRF protection, and destination auditability at the network boundary. Because the proxy evaluates the destination at connect time, after DNS resolution and immediately before it opens the upstream connection, it also narrows the gap a DNS-rebinding attack relies on between an earlier application-level DNS check and the actual outbound connection. A single proxy policy also gives operators one place to enforce destination rules, network segmentation, rate limits, or outbound allowlists without rebuilding Apus.

Apus does not ship, download, start, configure, or certify a proxy. You run the proxy technology that fits your environment; Apus routes its own HTTP and WebSocket clients through it.

Configuration

proxy:
  proxyUrl: http://127.0.0.1:3128

You can also set the URL through the environment:

APUS_PROXY_URL=http://127.0.0.1:3128 apus gateway run

proxy.proxyUrl takes precedence over APUS_PROXY_URL. A configured URL activates managed proxy routing; removing both URLs disables it.

Key Type Default Notes
proxy.proxyUrl string unset http:// or https:// forward proxy URL. Credentials embedded in the URL are treated as sensitive and redacted from snapshots/logs.
proxy.tls.caFile string unset CA bundle for verifying an https:// proxy endpoint signed by a private CA.
proxy.loopbackMode gateway-only | proxy | block gateway-only Controls loopback bypass behavior; see below.

For managed gateway services, store the URL in config so it survives reinstall, rather than relying on foreground env:

apus config set proxy.proxyUrl http://127.0.0.1:3128
apus gateway install --force
apus gateway start

The APUS_PROXY_URL env fallback is best for foreground runs. To use it with an installed service, put it in the service's durable environment ($APUS_STATE_DIR/.env, default ~/.apus/.env), then reinstall so launchd/systemd/Scheduled Tasks picks it up. This variable is copied into the generated service environment rather than tracked as a managed dotenv key, so systemd's restart-only managed dotenv refresh does not apply.

HTTPS proxy endpoint with a private CA

proxy:
  proxyUrl: https://proxy.corp.example:8443
  tls:
    caFile: /etc/apus/proxy-ca.pem

proxy.tls.caFile verifies the proxy endpoint's own TLS certificate. It is not a destination MITM trust setting, a client certificate, or a substitute for the proxy's destination policy. Use NODE_EXTRA_CA_CERTS instead only when the entire Node process must trust an additional CA from startup (for example, an enterprise TLS-inspection system re-signing every HTTPS destination certificate) — that variable is process-global and must be set before Node starts, so Apus cannot apply it mid-run the way it applies proxy.tls.caFile. Prefer proxy.tls.caFile for HTTPS proxy endpoint trust: it is scoped to managed proxy routing instead of the whole process.

apus config set proxy.proxyUrl https://proxy.corp.example:8443
apus config set proxy.tls.caFile /etc/apus/proxy-ca.pem
apus gateway run

How routing works

With a valid proxy URL, protected runtime processes (apus gateway run, apus node run, apus agent --local) route normal HTTP and WebSocket egress through the proxy:

Apus process
  fetch, node:http, node:https, WebSocket clients  -> operator proxy -> destination

Internally, Apus installs Proxyline as the process-level routing runtime. It covers fetch, undici-backed clients, node:http/node:https, common WebSocket clients, and helper-created CONNECT tunnels, and it replaces caller-provided Node HTTP agents so explicit agents (including axios, got, node-fetch, and similar Node-agent-based clients) cannot silently bypass the proxy.

The proxy URL scheme describes the hop from Apus to the proxy, not to the final destination:

Destination TLS is independent of proxy-endpoint TLS: for an HTTPS destination, Apus always asks the proxy for a CONNECT tunnel and starts destination TLS through that tunnel.

In the default gateway-only mode, loopback HTTP and WebSocket requests connect directly. This includes localhost, literal IPv4/IPv6 loopback addresses, and ephemeral listeners used by the native Codex inference relay. Apus installs this rule in the shared routing owner and replaces no_proxy/NO_PROXY with loopback-only entries for native child processes. External destinations still use the proxy; inherited bypasses for other destinations do not apply. On shutdown, Apus restores the prior proxy environment and resets cached routing state. The explicit proxy and block modes continue to clear no_proxy/NO_PROXY.

Some plugins own a custom transport that needs its own proxy wiring even with process-level routing active. Telegram's Bot API client uses its own HTTP/1 undici dispatcher and separately honors process proxy env plus the APUS_PROXY_URL fallback.

Gateway loopback mode

Local Gateway control-plane clients normally connect to a loopback WebSocket such as ws://127.0.0.1:18789. proxy.loopbackMode controls whether that traffic bypasses the managed proxy:

proxy:
  proxyUrl: http://127.0.0.1:3128
  loopbackMode: gateway-only # gateway-only, proxy, or block

A configured proxyUrl or APUS_PROXY_URL enables managed routing. Set proxy.enabled: false only as an advanced opt-out that keeps the URL stored without activating it.

Mode Behavior
gateway-only (default) Loopback HTTP and WebSocket traffic connects directly, including Gateway, browser CDP, update readiness, and native Codex inference relay listeners. No endpoint registration is required.
proxy Loopback traffic goes through the proxy. A remote proxy resolves 127.0.0.1/localhost against itself, so this mode can break local runtime listeners.
block Apus denies Gateway control-plane, browser CDP, and guarded local-provider loopback connections before opening a socket. Other requests use the proxy.

The implicit bypass covers localhost and literal loopback IP URLs. LAN, tailnet, private-network, and public hosts continue through the managed proxy. Application-level SSRF checks still apply to untrusted destinations.

Local Gateway HTTP health checks follow this policy too, including HTTPS checks used by Doctor and restart checks. HTTPS checks verify the configured Gateway certificate fingerprint on each connection; enabling a proxy does not disable that verification.

Update canary /startupz and /readyz checks use the same loopback routing policy. If a running canary never answers within the validation budget, the update records the observed failure as a warning, including the next troubleshooting step, and continues best effort.

Environment-only HTTP proxy routing honors no_proxy/NO_PROXY (lowercase takes precedence). These environment bypass lists do not override managed proxy policy.

WebChat or Codex fails after upgrading

The native Codex inference relay added in 2026.9.4 uses an ephemeral local listener. Older managed proxy routing could forward that hop to the external proxy, producing 502 Bad Gateway, Handshake not finished, or stream disconnected before completion for a 127.0.0.1 Responses URL.

Current default routing keeps these hops direct without changing config. Run apus doctor to test loopback connectivity under the active proxy. If the diagnostic fails, inspect apus config get proxy. For an explicit restrictive loopback mode, restore the default with:

apus config set proxy.loopbackMode gateway-only
apus gateway restart

For an older installation, the temporary workaround is to set proxy.enabled to false, keep HTTP_PROXY/HTTPS_PROXY pointing to the operator proxy, set NO_PROXY=127.0.0.1,localhost,::1, and restart the Gateway. Persist those environment variables in the Gateway service environment if needed.

Containers

For apus --container ... commands, Apus forwards APUS_PROXY_URL into the container-targeted child CLI when it is set. The URL must be reachable from inside the container — 127.0.0.1 there refers to the container itself, not the host. Apus rejects loopback proxy URLs for container-targeted commands unless you set APUS_CONTAINER_ALLOW_LOOPBACK_PROXY_URL=1 to explicitly override that check.

Related proxy terms

Validating the proxy

The proxy's destination policy is the actual security boundary; Apus cannot verify that your proxy blocks the right targets. Configure it to:

Validate from the same host/container/service account that runs Apus:

apus proxy validate --proxy-url http://127.0.0.1:3128

With a private-CA HTTPS proxy endpoint:

apus proxy validate --proxy-url https://proxy.corp.example:8443 --proxy-ca-file /etc/apus/proxy-ca.pem
Flag Purpose
--proxy-url <url> Validate this URL instead of resolving config/env.
--proxy-ca-file <path> CA bundle for an HTTPS proxy endpoint.
--allowed-url <url> Destination expected to succeed (repeatable).
--denied-url <url> Destination expected to be blocked (repeatable).
--apns-reachable Also verify the proxy can tunnel a direct sandbox APNs HTTP/2 check.
--apns-authority <url> Override the APNs authority checked with --apns-reachable.
--timeout-ms <ms> Per-request timeout.
--json Machine-readable output.

If no config, environment, or --proxy-url value is available, the command reports a config problem; pass --proxy-url for a one-off preflight before changing config.

With no --allowed-url/--denied-url, the default checks are: https://example.com/ must succeed, and a temporary loopback canary server the proxy must not reach must be blocked. The loopback check passes on a transport failure, or on a non-2xx response that lacks the canary's per-run token; it fails on a 2xx response missing the token (an unexpected success from something other than the canary) and, especially, on any response carrying the matching token, since that proves the proxy actually forwarded a loopback destination it should have denied. Custom --denied-url targets have no such canary token, so they are fail-closed: any HTTP response counts as reachable, and a transport error fails the check too, because Apus cannot confirm your proxy denied a reachable origin versus something else going wrong. Only the built-in loopback canary treats a transport error as proof of blocking. See apus proxy for the CLI-side statement of the same rule. --apns-reachable sends an intentionally invalid provider token, so a 403 InvalidProviderToken response counts as proof the tunnel reached Apple. The command exits 1 on any validation failure; proxy URL credentials are redacted from both text and JSON output.

{
  "ok": true,
  "config": {
    "enabled": true,
    "proxyUrl": "http://127.0.0.1:3128/",
    "source": "override",
    "errors": []
  },
  "checks": [
    { "kind": "allowed", "url": "https://example.com/", "ok": true, "status": 200 },
    { "kind": "apns", "url": "https://api.sandbox.push.apple.com", "ok": true, "status": 403 }
  ]
}

Manual curl check (the public request should succeed; the loopback and metadata requests should be blocked by the proxy itself — curl alone cannot distinguish a proxy denial from an unreachable origin the way apus proxy validate's built-in canary can):

curl -x http://127.0.0.1:3128 https://example.com/
curl -x http://127.0.0.1:3128 http://127.0.0.1/
curl -x http://127.0.0.1:3128 http://169.254.169.254/

Recommended blocked destinations

Starting denylist for any forward proxy, firewall, or egress policy. Apus's own SSRF classifier lives in src/platform/infra/net/ssrf.ts and packages/net-policy/src/ip.ts (BLOCKED_HOSTNAMES, BLOCKED_IPV4_SPECIAL_USE_RANGES, BLOCKED_IPV6_SPECIAL_USE_RANGES, the RFC 2544 benchmark prefix, and embedded-IPv4 handling for NAT64/6to4/Teredo/ISATAP/IPv4-mapped forms) — useful references, but Apus does not export or enforce these rules in your external proxy.

Range or host Why to block
127.0.0.0/8, localhost, localhost.localdomain IPv4 loopback
::1/128 IPv6 loopback
0.0.0.0/8, ::/128 Unspecified / this-network addresses
10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 RFC 1918 private networks
169.254.0.0/16, fe80::/10 Link-local, including common cloud metadata paths
169.254.169.254, metadata.google.internal Cloud metadata services
100.64.0.0/10 Carrier-grade NAT shared address space
198.18.0.0/15, 2001:2::/48 Benchmarking ranges
192.0.0.0/24, 192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24, 2001:db8::/32 Special-use and documentation ranges
224.0.0.0/4, ff00::/8 Multicast
240.0.0.0/4 Reserved IPv4
fc00::/7, fec0::/10 IPv6 local/private ranges
100::/64, 2001:20::/28 IPv6 discard and ORCHIDv2 ranges
64:ff9b::/96, 64:ff9b:1::/48 NAT64 prefixes with embedded IPv4
2002::/16, 2001::/32 6to4 and Teredo with embedded IPv4
::/96, ::ffff:0:0/96 IPv4-compatible and IPv4-mapped IPv6

Add any additional metadata hosts or reserved ranges your cloud provider or network platform documents.

Limits

Surface Managed proxy status
fetch, node:http, node:https, common WebSocket clients Routed through managed proxy hooks when configured.
APNs direct HTTP/2 Routed through the APNs managed CONNECT helper.
Gateway control-plane loopback Direct for loopback HTTP/WebSocket destinations in the default mode.
Debug proxy upstream forwarding Disabled while managed proxy mode is active unless explicitly enabled for local diagnostics.
IRC Raw TCP/TLS; not proxied by managed HTTP proxy mode. Set channels.irc.enabled: false if your deployment requires all egress through the forward proxy.
Other raw net, tls, or http2 client calls Must be classified by the raw socket guard before landing.

Related