openclaw - 💡(How to fix) Fix web_fetch blocked by private IP check when using proxy software (Stash/Clash/Surge) [1 participants]

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…
GitHub stats
openclaw/openclaw#57290Fetched 2026-04-08 01:51:31
View on GitHub
Comments
0
Participants
1
Timeline
1
Reactions
1
Participants
Timeline (top)
subscribed ×1

Error Message

  1. Observe the error: Blocked: resolves to private/internal/special-use IP address

Root Cause

web_fetch performs an IP safety check before initiating the HTTP request. It resolves the hostname, checks if the resulting IP falls within a private/special-use range, and blocks the request if so. The 198.18.0.0/15 range is classified as special-use and gets blocked, even though it's a legitimate virtual IP used by the proxy.

Setting HTTP_PROXY / HTTPS_PROXY environment variables (including in the LaunchAgent plist) does not help because the IP check happens at the DNS resolution stage, before any connection attempt or proxy routing.

Fix Action

Workaround

Using curl directly works fine since it connects to the virtual IP and the proxy handles it transparently. Node.js fetch() also works. The issue is specific to web_fetch's internal IP validation logic.

Code Example

Blocked: resolves to private/internal/special-use IP address
RAW_BUFFERClick to expand / collapse

Issue: web_fetch blocked when using proxy software (Stash/Clash/Surge) due to private IP check

Describe the bug

When proxy software like Stash, ClashX, or Surge is running on macOS in TUN mode, web_fetch consistently fails with:

Blocked: resolves to private/internal/special-use IP address

These proxy tools intercept DNS and resolve domains to virtual IPs in the 198.18.0.0/15 range (RFC 2544 benchmark testing range). The proxy's TUN interface then transparently forwards traffic to the real server. Browsers, curl, and Node.js fetch() all work correctly through this setup — but OpenClaw's web_fetch tool does not.

Root cause

web_fetch performs an IP safety check before initiating the HTTP request. It resolves the hostname, checks if the resulting IP falls within a private/special-use range, and blocks the request if so. The 198.18.0.0/15 range is classified as special-use and gets blocked, even though it's a legitimate virtual IP used by the proxy.

Setting HTTP_PROXY / HTTPS_PROXY environment variables (including in the LaunchAgent plist) does not help because the IP check happens at the DNS resolution stage, before any connection attempt or proxy routing.

Impact

  • web_fetch is completely unusable for users running Stash, ClashX, Surge, or similar proxy tools in TUN/TAP mode
  • This affects potentially millions of users in regions where proxy software is standard (e.g., China mainland, corporate networks)
  • web_search (Gemini API) works fine since it's server-side, but OpenClaw's automatic citation URL verification via web_fetch fails silently for every result
  • The gateway logs are flooded with [security] blocked URL fetch warnings

Reproduction

  1. Install and enable Stash/ClashX/Surge in TUN mode on macOS
  2. Verify DNS resolves to virtual IPs: nslookup www.bbc.com198.18.x.x
  3. Run openclaw and use the web_fetch tool on any URL
  4. Observe the error: Blocked: resolves to private/internal/special-use IP address

Workaround

Using curl directly works fine since it connects to the virtual IP and the proxy handles it transparently. Node.js fetch() also works. The issue is specific to web_fetch's internal IP validation logic.

Suggested fix

When HTTP_PROXY or HTTPS_PROXY environment variables are set, web_fetch should skip the private IP safety check. The proxy is already acting as a trust boundary — checking IPs behind the proxy is unnecessary and breaks legitimate proxy usage.

Alternative approaches:

  • Add a config option like tools.web.fetch.skipIpCheck: true or tools.web.fetch.trustProxy: true
  • Add 198.18.0.0/15 to the allowlist since it's commonly used by proxy software
  • Allow users to configure a list of IP ranges that should not be blocked

Environment

  • OpenClaw version: 2026.3.28
  • OS: macOS 26.3.1 (arm64)
  • Node.js: 25.8.2
  • Proxy: Stash (TUN mode, port 7890)
  • DNS resolution: 198.18.x.x (Stash virtual IP)

extent analysis

Fix Plan

To resolve the issue, we can modify the web_fetch tool to skip the private IP safety check when HTTP_PROXY or HTTPS_PROXY environment variables are set. Here are the steps:

  • Modify the web_fetch function to check for the presence of HTTP_PROXY or HTTPS_PROXY environment variables.
  • If either variable is set, skip the private IP safety check.
  • Alternatively, add a config option like tools.web.fetch.skipIpCheck: true or tools.web.fetch.trustProxy: true to allow users to control this behavior.

Example code:

const fetch = require('node-fetch');
const proxyUrl = process.env.HTTP_PROXY || process.env.HTTPS_PROXY;

function webFetch(url) {
  // Check if proxy is set
  if (proxyUrl) {
    // Skip private IP safety check
    return fetch(url, { proxy: proxyUrl });
  } else {
    // Perform private IP safety check
    const ip = require('ipaddr.js').parse(url);
    if (ip.range() === 'private' || ip.range() === 'special') {
      throw new Error('Blocked: resolves to private/internal/special-use IP address');
    }
    return fetch(url);
  }
}

Alternatively, you can add a config option to allow users to control this behavior:

const config = require('./config.json');

function webFetch(url) {
  if (config.tools.web.fetch.skipIpCheck || config.tools.web.fetch.trustProxy) {
    return fetch(url);
  } else {
    // Perform private IP safety check
    const ip = require('ipaddr.js').parse(url);
    if (ip.range() === 'private' || ip.range() === 'special') {
      throw new Error('Blocked: resolves to private/internal/special-use IP address');
    }
    return fetch(url);
  }
}

Verification

To verify that the fix worked, you can test the web_fetch tool with a URL that previously failed due to the private IP safety check. If the fix is successful, the web_fetch tool should now be able to fetch the URL without throwing an error.

Extra Tips

  • Make sure to update the web_fetch tool to handle cases where the HTTP_PROXY or HTTPS_PROXY environment variables are not set.
  • Consider adding additional logging or debugging statements to help diagnose any issues that may arise.
  • If you choose to add a config option, make sure to document it clearly and provide examples of how to use it.

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