Tencent/WeKnora · error

URL rejected by SSRF policy: %w

Error message

URL rejected by SSRF policy: %w

What it means

DownloadBytes runs every URL through ValidateURLForSSRF before fetching; if the SSRF policy rejects the target (private/loopback/link-local IPs, disallowed hosts, etc.) the error is wrapped as "URL rejected by SSRF policy". This protects against server-side request forgery against internal networks.

Source

Thrown at internal/utils/httputil.go:23

	"io"
	"net/http"
	"strings"
	"time"
)

var defaultHTTPClient = NewSSRFSafeHTTPClient(SSRFSafeHTTPClientConfig{
	Timeout:      60 * time.Second,
	MaxRedirects: 10,
})

// DownloadBytes fetches the content at the given HTTP(S) URL and returns the
// raw bytes. It reuses a package-level http.Client with a 60-second timeout.
func DownloadBytes(url string) ([]byte, error) {
	if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
		return nil, fmt.Errorf("unsupported URL scheme: %s", url)
	}
	if err := ValidateURLForSSRF(url); err != nil {
		return nil, fmt.Errorf("URL rejected by SSRF policy: %w", err)
	}
	resp, err := defaultHTTPClient.Get(url)
	if err != nil {
		return nil, fmt.Errorf("HTTP GET: %w", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("HTTP %d for %s", resp.StatusCode, url)
	}
	data, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("read body: %w", err)
	}
	return data, nil
}

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Use a public, externally reachable URL
  2. If internal fetching is legitimately required, use an approved internal client that bypasses the SSRF guard, not DownloadBytes
  3. Check ValidateURLForSSRF's exact policy and the wrapped inner error to see which rule fired
  4. For local development, run the target on a public test endpoint or mock the HTTP layer

Example fix

// before
DownloadBytes("http://localhost:8080/asset")
// after
DownloadBytes("https://cdn.example.com/asset")
Defensive patterns

Strategy: try-catch

Validate before calling

u, err := url.Parse(raw)
if err != nil { return err }
ip, err := net.LookupIP(u.Hostname())
if err != nil { return err }
for _, a := range ip {
    if a.IsLoopback() || a.IsPrivate() || a.IsLinkLocalUnicast() {
        return fmt.Errorf("URL points at a blocked/internal address")
    }
}

Type guard

func isPublicURL(raw string) bool {
    u, err := url.Parse(raw)
    if err != nil || (u.Scheme != "http" && u.Scheme != "https") { return false }
    ip, err := net.LookupIP(u.Hostname())
    if err != nil { return false }
    for _, a := range ip {
        if a.IsLoopback() || a.IsPrivate() || a.IsLinkLocalUnicast() { return false }
    }
    return true
}

Try / catch

data, err := DownloadBytes(url)
if err != nil {
    var ssrfErr *fmt.wrapError
    if strings.Contains(err.Error(), "URL rejected by SSRF policy") {
        // do NOT retry; the target is policy-blocked — use a public URL
        return fmt.Errorf("target blocked: %w", err)
    }
}

Prevention

When it happens

Trigger: Calling DownloadBytes with a URL that resolves to or points at a blocked target: localhost/127.0.0.1, 10.x/172.16.x/192.168.x private addresses, 169.254.x metadata endpoints, or any host denied by the SSRF policy.

Common situations: Fetching user-supplied webhook/avatar/import URLs that point at internal services, testing against a locally running server, misconfigured internal-only endpoints, or DNS rebinding to internal IPs.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/ae6c03f24b44e961. Report an issue: GitHub.