{"id":"697cf330a666982d","repo":"gofiber/fiber","slug":"proxy-upstream-host-resolves-to-a-blocked-address","errorCode":null,"errorMessage":"proxy: upstream host resolves to a blocked address","messagePattern":"proxy: upstream host resolves to a blocked address","errorType":"exception","errorClass":"ErrUpstreamHostBlocked","httpStatus":null,"severity":"error","filePath":"middleware/proxy/security.go","lineNumber":60,"sourceCode":"// allocate []byte(\"https\") on every hop.\nvar httpsSchemeBytes = []byte(schemeHTTPS)\n\n// Sentinel errors returned when an upstream target violates the configured\n// proxy security policy.\nvar (\n\t// ErrUpstreamSchemeNotAllowed is returned when the proxied URL uses a\n\t// scheme outside the configured allowlist (default: http, https).\n\tErrUpstreamSchemeNotAllowed = errors.New(\"proxy: upstream scheme is not allowed\")\n\n\t// ErrUpstreamHostInvalid is returned when the proxied URL is missing a\n\t// host or cannot be parsed.\n\tErrUpstreamHostInvalid = errors.New(\"proxy: upstream host is empty or invalid\")\n\n\t// ErrUpstreamHostBlocked is returned when the proxied URL resolves to\n\t// an address inside a blocked range (loopback, RFC 1918 private,\n\t// link-local, multicast, unspecified, or CGNAT) and AllowPrivateIPs\n\t// is false.\n\tErrUpstreamHostBlocked = errors.New(\"proxy: upstream host resolves to a blocked address\")\n\n\t// ErrRedirectDowngrade is returned when DoRedirects encounters a\n\t// redirect from an HTTPS upstream to a plaintext HTTP target and\n\t// AllowHTTPSDowngrade is false.\n\tErrRedirectDowngrade = errors.New(\"proxy: HTTPS to HTTP redirect blocked\")\n)\n\n// SecurityPolicy controls runtime security restrictions applied to the\n// proxy.Do, proxy.Forward, proxy.DoRedirects, proxy.DoTimeout, and\n// proxy.DoDeadline runtime helpers as well as Balancer instances that\n// do not supply their own policy via Config.SecurityPolicy.\ntype SecurityPolicy struct {\n\t// AllowedSchemes restricts the URL schemes accepted as upstream\n\t// targets. Empty defaults to []string{schemeHTTP, schemeHTTPS}.\n\tAllowedSchemes []string\n\n\t// AllowPrivateIPs allows upstream hosts to resolve to loopback,\n\t// private (RFC 1918), link-local, multicast, unspecified, or CGNAT","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/gofiber/fiber/blob/9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c/middleware/proxy/security.go#L42-L78","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\napp.Use(proxy.New(\"http://localhost:8080\"))\n\n// after — explicit opt-in for a trusted internal target\napp.Use(proxy.New(\"http://localhost:8080\", proxy.Config{\n    SecurityPolicy: &proxy.SecurityPolicy{AllowPrivateIPs: true},\n}))","handlingStrategy":"validation","validationCode":"// Resolve and pre-check the upstream host against the same blocklist logic.\nfunc isBlocked(host string) bool {\n    ips, err := net.DefaultResolver.LookupIPAddr(context.Background(), host)\n    if err != nil {\n        return true // treat unresolvable as blocked\n    }\n    for _, ip := range ips {\n        if ip.IP.IsLoopback() || ip.IP.IsPrivate() || ip.IP.IsLinkLocalUnicast() ||\n            ip.IP.IsLinkLocalMulticast() || ip.IP.IsUnspecified() || ip.IP.IsMulticast() {\n            return true\n        }\n        if v4 := ip.IP.To4(); v4 != nil && v4[0] == 100 && v4[1] >= 64 && v4[1] <= 127 {\n            return true // CGNAT\n        }\n    }\n    return false\n}\n\nif isBlocked(targetHost) {\n    // refuse to proxy, or set AllowPrivateIPs if trusted\n}","typeGuard":null,"tryCatchPattern":"if _, err := proxy.Do(req, target); err != nil {\n    if errors.Is(err, proxy.ErrUpstreamHostBlocked) {\n        // upstream resolved to a private/loopback/metadata address\n        return fiber.NewError(fiber.StatusBadGateway, \"upstream not reachable\")\n    }\n    return err\n}","preventionTips":["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."],"tags":["proxy","ssrf","security","dns","network"],"analyzedSha":"9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c","analyzedAt":"2026-08-04T21:44:03.395Z","schemaVersion":2}