phacility/phabricator · error · Exception

URI "%s" is not a valid fetchable resource. The domain "%s"

Error message

URI "%s" is not a valid fetchable resource. The domain "%s" resolves to the address "%s", which is blacklisted for outbound requests.

What it means

Final gate of requireValidRemoteURIForFetch(): after DNS resolution, every returned address is checked against the outbound blacklist (isBlacklistedOutboundAddress, driven by `phabricator.serious-business`-adjacent security config - concretely the outbound-address blacklist covering loopback, private ranges, link-local, etc.). If any A/AAAA record falls in a blacklisted range, the fetch is refused. This is Phabricator's core SSRF defense: it stops users from making the server fetch 127.0.0.1, 169.254.169.254 (cloud metadata), or RFC1918 internals via a DNS name.

Source

Thrown at src/infrastructure/env/PhabricatorEnv.php:835

        pht(
          'URI "%s" is not a valid fetchable resource. A valid fetchable '.
          'resource URI must specify a domain.',
          $raw_uri));
    }

    $addresses = gethostbynamel($domain);
    if (!$addresses) {
      throw new Exception(
        pht(
          'URI "%s" is not a valid fetchable resource. The domain "%s" could '.
          'not be resolved.',
          $raw_uri,
          $domain));
    }

    foreach ($addresses as $address) {
      if (self::isBlacklistedOutboundAddress($address)) {
        throw new Exception(
          pht(
            'URI "%s" is not a valid fetchable resource. The domain "%s" '.
            'resolves to the address "%s", which is blacklisted for '.
            'outbound requests.',
            $raw_uri,
            $domain,
            $address));
      }
    }

    $resolved_uri = clone $uri;
    $resolved_uri->setDomain(head($addresses));

    return array($resolved_uri, $domain);
  }


  /**

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. If the fetch is genuinely internal and trusted, adjust the outbound address blacklist config (outbound address blacklist keys in cluster/security settings) to permit the specific internal range - narrow it to the exact CIDR needed, never disable the whole blacklist.
  2. Otherwise point the URL at a genuinely public host.
  3. Never work around by pre-resolving and passing an IP - the same per-address check runs on every resolved address.

Example fix

# before: hostname resolves to a blacklisted private address
$ getent hosts ci.internal
10.0.0.15      ci.internal
# fetch of https://ci.internal/... throws: blacklisted for outbound requests

# after: whitelist the exact internal range (deliberate, narrow change)
$ ./bin/config set cluster.addresses ...   # unrelated
# adjust the outbound blacklist to exclude 10.0.0.15/32 only, keep the rest
Defensive patterns

Strategy: try-catch

Validate before calling

$domain = (new PhutilURI($url))->getDomain();
foreach ((array) gethostbynamel($domain) as $addr) {
  if (PhabricatorEnv::isBlacklistedOutboundAddress($addr)) {
    // resolves to a private/blacklisted range: refuse before fetching
    return array('err' => 'Blocked outbound address');
  }
}

Try / catch

try {
  PhabricatorEnv::requireValidRemoteURIForFetch($url, array('http', 'https'));
} catch (Exception $ex) {
  if (strpos($ex->getMessage(), 'blacklisted for outbound') !== false) {
    // deliberate SSRF block: report, never bypass by proxying elsewhere
    phlog(pht('Blocked SSRF attempt: %s', $url));
    return new Aphront403Response();
  }
  throw $ex;
}

Prevention

When it happens

Trigger: requireValidRemoteURIForFetch() on a hostname that resolves (or multi-homed resolves partly) to 127.0.0.1, 10.x, 192.168.x, 169.254.169.254, ::1, or any range in the configured outbound blacklist - e.g. a developer pointing an image/import URL at 'localhost.attacker.com' which DNS-maps to 127.0.0.1.

Common situations: Legitimate intranet fetches (Gravatar-on-prem mirrors, internal CI links) blocked because the target lives in RFC1918 space; post-exploitation or security scans testing SSRF; split-horizon DNS where the public name resolves internally to a private IP from the Phabricator host's view.

Related errors


AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21). Data as JSON: /api/errors/a90df988434f6f39. Report an issue: GitHub.