gofiber/fiber · critical · ErrUpstreamHostInvalid

proxy: upstream host is empty or invalid

Error message

proxy: upstream host is empty or invalid

What it means

Returned by proxy.parseUpstream/parseUpstreamScheme (security.go:54, 290, 334) and validateHostForSSRF when the upstream URL has an empty host (e.g. 'http://:8080' where Hostname() is empty) or the raw upstream string is empty/whitespace. The proxy requires a concrete hostname to dial; an empty host is both a misconfiguration and an SSRF risk. Fires per-request for Do/Forward and as a startup panic for DomainForward/BalancerForward.

Source

Thrown at middleware/proxy/security.go:54

// SecurityPolicy.AllowedSchemes field, so nothing outside this file can
// mutate the backing array.
var defaultAllowedSchemes = []string{schemeHTTP, schemeHTTPS}

// httpsSchemeBytes is the byte form of "https" used by redirect
// downgrade checks. Stored once so the resolveRedirect hot path doesn't
// 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 {

View on GitHub (pinned to 9a4c7e57fe)

Solutions

  1. Ensure the upstream URL includes a non-empty hostname or IP (e.g. 'http://backend:8080').
  2. Validate the upstream string is non-empty before passing it to proxy functions.
  3. For env-driven upstreams, fail fast at startup if the env var is empty rather than per-request.

Example fix

// before
upstream := os.Getenv("UPSTREAM_URL") // may be empty
proxy.Do(c, upstream)
// after
upstream := os.Getenv("UPSTREAM_URL")
if upstream == "" {
  return c.SendStatus(fiber.StatusBadGateway)
}
proxy.Do(c, upstream)
Defensive patterns

Strategy: validation

Validate before calling

// Validate upstream host is non-empty before proxying
u, err := url.Parse(upstream)
if err != nil || u.Hostname() == "" {
    return c.Status(fiber.StatusBadGateway).SendString("invalid upstream")
}
return proxy.Do(c, upstream)

Prevention

When it happens

Trigger: Passing an empty string or whitespace to proxy.Do/Forward; configuring an upstream like 'http://:8080' (port but no host); a URL like 'http:///path' (empty host); or a dynamically-built upstream where the host variable is unset. validateHostForSSRF (security.go:390) also returns it when the resolved host is empty.

Common situations: Environment variable for the upstream host is unset (empty string); templated upstream URL with a missing variable; copy-paste error dropping the hostname; upstream built from request input where the host part is absent; IPv6 URL missing brackets.

Related errors


AI-assisted analysis of gofiber/fiber@9a4c7e57fe (2026-08-04). Data as JSON: /data/errors/e3b7c5cbf12288f2.json. Report an issue: GitHub.