gofiber/fiber · error · ErrUpstreamHostBlocked
proxy: upstream host resolves to a blocked address
Error message
proxy: upstream host resolves to a blocked address
What it means
Returned as ErrUpstreamHostBlocked by the proxy middleware when an upstream URL resolves to an address inside a blocked range (loopback, RFC 1918 private, link-local including 169.254.169.254 cloud-metadata, multicast, unspecified, or RFC 6598 CGNAT) and SecurityPolicy.AllowPrivateIPs is false. The check runs both up front (validateUpstream/validateHostForSSRF) and again at dial time (newSSRFDialer / guardedDial) to defeat DNS-rebinding. It is Fiber's primary SSRF defense, on by default.
Source
Thrown at middleware/proxy/security.go:60
// allocate []byte("https") on every hop.
var httpsSchemeBytes = []byte(schemeHTTPS)
// Sentinel errors returned when an upstream target violates the configured
// proxy security policy.
var (
// ErrUpstreamSchemeNotAllowed is returned when the proxied URL uses a
// scheme outside the configured allowlist (default: http, https).
ErrUpstreamSchemeNotAllowed = errors.New("proxy: upstream scheme is not allowed")
// ErrUpstreamHostInvalid is returned when the proxied URL is missing a
// host or cannot be parsed.
ErrUpstreamHostInvalid = errors.New("proxy: upstream host is empty or invalid")
// ErrUpstreamHostBlocked is returned when the proxied URL resolves to
// an address inside a blocked range (loopback, RFC 1918 private,
// link-local, multicast, unspecified, or CGNAT) and AllowPrivateIPs
// is false.
ErrUpstreamHostBlocked = errors.New("proxy: upstream host resolves to a blocked address")
// ErrRedirectDowngrade is returned when DoRedirects encounters a
// redirect from an HTTPS upstream to a plaintext HTTP target and
// AllowHTTPSDowngrade is false.
ErrRedirectDowngrade = errors.New("proxy: HTTPS to HTTP redirect blocked")
)
// SecurityPolicy controls runtime security restrictions applied to the
// proxy.Do, proxy.Forward, proxy.DoRedirects, proxy.DoTimeout, and
// proxy.DoDeadline runtime helpers as well as Balancer instances that
// do not supply their own policy via Config.SecurityPolicy.
type SecurityPolicy struct {
// AllowedSchemes restricts the URL schemes accepted as upstream
// targets. Empty defaults to []string{schemeHTTP, schemeHTTPS}.
AllowedSchemes []string
// AllowPrivateIPs allows upstream hosts to resolve to loopback,
// private (RFC 1918), link-local, multicast, unspecified, or CGNATView on GitHub (pinned to 9a4c7e57fe)
Solutions
- If the internal target is trusted and intentional, opt in with SecurityPolicy{AllowPrivateIPs: true} via Config.SecurityPolicy or proxy.WithSecurityPolicy() — but only for that scope, since it re-enables SSRF to cloud-metadata/internal services.
- Use a public hostname or public IP for the upstream so resolution stays outside blocked ranges.
- Keep AllowPrivateIPs false and never forward client-supplied URLs through proxy.Do; validate/allowlist the host before proxying.
- For a Balancer with a custom Config.Client (*fasthttp.LBClient), note the dialers are not guarded by Fiber — install your own SSRF dialer there.
Example fix
// before
app.Use(proxy.New("http://localhost:8080"))
// after — explicit opt-in for a trusted internal target
app.Use(proxy.New("http://localhost:8080", proxy.Config{
SecurityPolicy: &proxy.SecurityPolicy{AllowPrivateIPs: true},
})) Defensive patterns
Strategy: validation
Validate before calling
// Resolve and pre-check the upstream host against the same blocklist logic.
func isBlocked(host string) bool {
ips, err := net.DefaultResolver.LookupIPAddr(context.Background(), host)
if err != nil {
return true // treat unresolvable as blocked
}
for _, ip := range ips {
if ip.IP.IsLoopback() || ip.IP.IsPrivate() || ip.IP.IsLinkLocalUnicast() ||
ip.IP.IsLinkLocalMulticast() || ip.IP.IsUnspecified() || ip.IP.IsMulticast() {
return true
}
if v4 := ip.IP.To4(); v4 != nil && v4[0] == 100 && v4[1] >= 64 && v4[1] <= 127 {
return true // CGNAT
}
}
return false
}
if isBlocked(targetHost) {
// refuse to proxy, or set AllowPrivateIPs if trusted
} Try / catch
if _, err := proxy.Do(req, target); err != nil {
if errors.Is(err, proxy.ErrUpstreamHostBlocked) {
// upstream resolved to a private/loopback/metadata address
return fiber.NewError(fiber.StatusBadGateway, "upstream not reachable")
}
return err
} Prevention
- Never forward user-supplied URLs through proxy.Do without an allowlist.
- Keep AllowPrivateIPs false unless you explicitly proxy to trusted internal services.
- For Balancer with a custom Config.Client (*fasthttp.LBClient), install your own SSRF-guarding dialer — Fiber does not guard that path.
When it happens
Trigger: Calling proxy.Do/Forward/DoRedirects/DoTimeout/DoDeadline, or serving through a Balancer, with a target host that resolves to a private/loopback IP — e.g. proxying to 'http://localhost:8080', 'http://10.0.0.5', 'http://169.254.169.254' (AWS metadata), or a hostname whose DNS A record returns 127.0.0.1. Also triggered if a client-controlled URL (request param) is forwarded without filtering.
Common situations: Dev/staging where the upstream is an internal service (e.g. proxying to a localhost backend during local development); SSRF hardening kicking in after upgrading Fiber to a version that made AllowPrivateIPs default false; forwarding user-supplied URLs in an image-proxy/url-preview feature; multi-container setups where the upstream is on a private docker network.
Related errors
- proxy: upstream scheme is not allowed
- ErrUpstreamSchemeNotAllowed
- ErrUpstreamHostBlocked
- proxy: upstream host is empty or invalid
- proxy: HTTPS to HTTP redirect blocked
AI-assisted analysis of gofiber/fiber@9a4c7e57fe (2026-08-04).
Data as JSON: /data/errors/697cf330a666982d.json.
Report an issue: GitHub.