gofiber/fiber · error
ErrUpstreamHostBlocked
ErrUpstreamHostBlocked
Error message
%w: %s
What it means
Returned by validateUpstreamForBalancer at Balancer construction when a statically configured upstream host is an IP literal that falls in a blocked range (loopback, RFC1918 private, link-local, multicast, unspecified, CGNAT, or blocked IPv6 transition ranges) and AllowPrivateIPs is false.
Source
Thrown at middleware/proxy/security.go:355
}
// validateUpstreamForBalancer validates a statically configured Balancer
// upstream. It enforces the scheme allowlist and rejects IP-literal hosts
// in blocked ranges, but defers hostname resolution to the SSRF-guarded
// dialer (see newSSRFDialer). Deferring DNS keeps a transient resolver
// failure at startup from panicking the application (e.g. crash loops in
// container orchestrators) and re-checks the resolved IP on every dial,
// which also defeats DNS-rebinding.
func validateUpstreamForBalancer(raw string, policy SecurityPolicy) (*url.URL, error) {
u, err := parseUpstreamScheme(raw, policy)
if err != nil {
return nil, err
}
if policy.AllowPrivateIPs {
return u, nil
}
if ip := net.ParseIP(trimBrackets(u.Hostname())); ip != nil && isBlockedIP(ip) {
return nil, fmt.Errorf("%w: %s", ErrUpstreamHostBlocked, ip)
}
return u, nil
}
// schemeAllowed reports whether scheme is on the allowlist. An empty
// allowlist falls back to the secure defaults.
func schemeAllowed(scheme string, allowed []string) bool {
if scheme == "" {
return false
}
if len(allowed) == 0 {
allowed = defaultAllowedSchemes
}
for _, s := range allowed {
if utils.EqualFold(s, scheme) {
return true
}
}View on GitHub (pinned to 9a4c7e57fe)
Solutions
- If the private backend is intentional and trusted, enable AllowPrivateIPs:true on the SecurityPolicy (understand it widens SSRF exposure).
- Otherwise point the Balancer at a public IP or resolvable public hostname.
- Re-check the server list for accidentally pasted loopback/metadata addresses.
Example fix
// before: private IP blocked by default SSRF policy
balancer := proxy.Balancer(proxy.Config{
Servers: []string{"http://10.0.0.5:8080"},
})
// after: explicitly opt in for a trusted internal backend
policy := proxy.DefaultSecurityPolicy()
policy.AllowPrivateIPs = true
balancer := proxy.Balancer(proxy.Config{
Servers: []string{"http://10.0.0.5:8080"},
SecurityPolicy: &policy,
}) Defensive patterns
Strategy: validation
Validate before calling
// At config time, decide policy for private backends explicitly.
func balancerPolicy(servers []string, allowPrivate bool) *proxy.SecurityPolicy {
p := proxy.DefaultSecurityPolicy()
p.AllowPrivateIPs = allowPrivate // true only for trusted internal backends
return &p
} Try / catch
// validateUpstreamForBalancer runs at Balancer construction; wrap New
// and report the error clearly at startup.
b, err := proxy.Balancer(proxy.Config{Servers: svrs, SecurityPolicy: &p})
if err != nil {
if errors.Is(err, proxy.ErrUpstreamHostBlocked) {
log.Fatal().Err(err).Msg("backend IP blocked; set AllowPrivateIPs if trusted")
}
log.Fatal().Err(err).Msg("balancer init failed")
} Prevention
- Audit Balancer server entries for loopback/private/metadata IPs.
- Enable AllowPrivateIPs only for genuinely trusted internal backends.
- Prefer public hostnames or properly scoped service DNS.
When it happens
Trigger: A Balancer server entry is a numeric IP like 127.0.0.1, 10.0.0.5, 169.254.169.254, or 100.64.0.1 while AllowPrivateIPs is false (the default). The IP-literal shortcut at security.go:354 rejects it without DNS.
Common situations: Pointing a Balancer at an internal/private service during local development or inside a cluster; using a cloud metadata IP by mistake; forgetting to set AllowPrivateIPs when the backend genuinely is on a private network.
Related errors
- proxy: upstream scheme is not allowed
- proxy: upstream host is empty or invalid
- proxy: upstream host resolves to a blocked address
- ErrUpstreamSchemeNotAllowed
- csrf: referer does not match host or trusted origins
AI-assisted analysis of gofiber/fiber@9a4c7e57fe (2026-08-04).
Data as JSON: /data/errors/cee29237fc14327e.json.
Report an issue: GitHub.