hermes - 💡(How to fix) Fix [Bug] urllib / internal HTTP to localhost fails (503) when HTTP_PROXY env points to local proxy

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

When HTTP_PROXY / HTTPS_PROXY is set in the environment (which is the default for many users running v2ray / clash / privoxy / mitmproxy / corporate proxies, especially on Windows where these tools are the standard outbound mechanism), Hermes's internal HTTP calls to localhost / 127.0.0.1 route through the proxy instead of going direct.

Result: gateway health checks, internal API calls, dashboard probes, and any other localhost-bound HTTP fails with 503 / connection refused / proxy-rejection, even though the local service is up.

Error Message

Linux / macOS / Windows — any platform with a local HTTP proxy

export HTTP_PROXY=http://127.0.0.1:10808 python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8765/health').read()"

urllib.error.HTTPError: 503 ... (proxy rejecting localhost-to-localhost relay)

Root Cause

  • Platforms: all (Linux / macOS / Windows), but disproportionately bites Windows users because of the v2ray/clash ecosystem
  • Frequency: 100% of users with HTTP_PROXY env set and any Hermes internal HTTP to localhost
  • Symptom: 503 / "Bad Gateway" / connection-refused from the proxy when Hermes tries to reach 127.0.0.1:<port>

Fix Action

Fix / Workaround

Workaround currently used downstream

Code Example

# Linux / macOS / Windows — any platform with a local HTTP proxy
export HTTP_PROXY=http://127.0.0.1:10808
python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8765/health').read()"
# urllib.error.HTTPError: 503 ... (proxy rejecting localhost-to-localhost relay)

---

# Fixed:
python -c "
import urllib.request
opener = urllib.request.build_opener(urllib.request.ProxyHandler({}))
opener.open('http://127.0.0.1:8765/health').read()
print('ok')
"
RAW_BUFFERClick to expand / collapse

Summary

When HTTP_PROXY / HTTPS_PROXY is set in the environment (which is the default for many users running v2ray / clash / privoxy / mitmproxy / corporate proxies, especially on Windows where these tools are the standard outbound mechanism), Hermes's internal HTTP calls to localhost / 127.0.0.1 route through the proxy instead of going direct.

Result: gateway health checks, internal API calls, dashboard probes, and any other localhost-bound HTTP fails with 503 / connection refused / proxy-rejection, even though the local service is up.

Severity

  • Platforms: all (Linux / macOS / Windows), but disproportionately bites Windows users because of the v2ray/clash ecosystem
  • Frequency: 100% of users with HTTP_PROXY env set and any Hermes internal HTTP to localhost
  • Symptom: 503 / "Bad Gateway" / connection-refused from the proxy when Hermes tries to reach 127.0.0.1:<port>

Reproduction

# Linux / macOS / Windows — any platform with a local HTTP proxy
export HTTP_PROXY=http://127.0.0.1:10808
python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8765/health').read()"
# urllib.error.HTTPError: 503 ... (proxy rejecting localhost-to-localhost relay)
# Fixed:
python -c "
import urllib.request
opener = urllib.request.build_opener(urllib.request.ProxyHandler({}))
opener.open('http://127.0.0.1:8765/health').read()
print('ok')
"

Why this matters for Hermes

Several Hermes components do localhost HTTP under the hood:

  • Gateway health / readiness probes
  • Profile-internal API calls between gateway components
  • Dashboard / web UI fetching state from the local gateway
  • Any platform adapter that hits a local webhook receiver

When any of these are launched in a shell with HTTP_PROXY set, they fail in a way that's hard to debug — the error message says "503" but the local service is fine; the user doesn't immediately think "proxy".

Workaround currently used downstream

Two options:

  1. Per-call: build a urllib.request.opener with ProxyHandler({}) for any localhost-bound HTTP — this is what I do in the gateway/bridge integration
  2. Process-level: os.environ.setdefault("NO_PROXY", "127.0.0.1,localhost,::1") at gateway startup, before any HTTP lib is imported

Option 2 is cheaper but only works if Hermes can rely on NO_PROXY being read by all transport libraries (some don't honor it consistently across versions).

Proposed fix

Pick one of:

  1. Default NO_PROXY augmentation: at gateway startup, if HTTP_PROXY is set and NO_PROXY doesn't already include 127.0.0.1/localhost, prepend them. Document this clearly.
  2. Explicit localhost bypass in internal HTTP helpers: any Hermes-internal HTTP client uses ProxyHandler({}) (urllib) or proxies={"http":None,"https":None} (requests) or trust_env=False (httpx) for localhost destinations.
  3. Both (defense in depth).

I lean toward (3): set NO_PROXY defensively at startup, and also use explicit bypass in internal helpers, so neither alone has to be perfect.

Happy to PR if there's interest. Filing as a bug for triage first.

Related: #31385 (bridge), #31417 (StreamReader), and sibling Windows bugs I'm filing alongside this.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING