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
- Use a public, externally reachable URL
- If internal fetching is legitimately required, use an approved internal client that bypasses the SSRF guard, not DownloadBytes
- Check ValidateURLForSSRF's exact policy and the wrapped inner error to see which rule fired
- 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
- Never fetch user-supplied URLs pointing at private/loopback ranges
- Use public endpoints or an approved internal fetch path for internal resources
- Log the inner SSRF-policy cause to diagnose which rule rejected the URL
- Do not attempt retries against SSRF-rejected targets — retrying cannot succeed
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
- docreader address failed SSRF validation: %w
- zip URL blocked by SSRF check: %v
- redirect blocked: target URL failed SSRF validation
- %w: invalid scheme %s
- %w: %w
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/ae6c03f24b44e961.
Report an issue: GitHub.