gofiber/fiber · error

ErrUpstreamSchemeNotAllowed

ErrUpstreamSchemeNotAllowed

Error message

%w: %q

What it means

Returned by parseUpstreamScheme when the upstream URL's scheme is not in the configured AllowedSchemes allowlist (default: http, https). Wraps the ErrUpstreamSchemeNotAllowed sentinel.

Source

Thrown at middleware/proxy/security.go:331

		return u, nil
	}
	if err := validateHostForSSRF(u.Hostname()); err != nil {
		return nil, err
	}
	return u, nil
}

// parseUpstreamScheme parses raw and enforces the scheme allowlist and
// host presence without performing any DNS resolution. url.Parse can
// leave Host set while Hostname() is empty (e.g. "http://:8080"), so the
// presence check uses Hostname.
func parseUpstreamScheme(raw string, policy SecurityPolicy) (*url.URL, error) {
	u, err := parseUpstream(raw)
	if err != nil {
		return nil, err
	}
	if !schemeAllowed(u.Scheme, policy.AllowedSchemes) {
		return nil, fmt.Errorf("%w: %q", ErrUpstreamSchemeNotAllowed, u.Scheme)
	}
	if u.Hostname() == "" {
		return nil, ErrUpstreamHostInvalid
	}
	return u, nil
}

// 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

View on GitHub (pinned to 9a4c7e57fe)

Solutions

  1. Ensure the upstream uses http or https.
  2. If a different scheme is genuinely required, explicitly add it via SecurityPolicy.AllowedSchemes (review the security implications first).
  3. Reject user-controlled upstream input that specifies a scheme other than http/https.

Example fix

// before: user-supplied upstream may use file://
proxy.Do(ctx, userInput)

// after: force http(s) only
u, _ := url.Parse(userInput)
if u.Scheme != "http" && u.Scheme != "https" {
    return fiber.NewError(fiber.StatusBadRequest, "scheme not allowed")
}
proxy.Do(ctx, u.String())
Defensive patterns

Strategy: validation

Validate before calling

// Reject any scheme other than http/https before proxying.
func allowedScheme(raw string) error {
    u, err := url.Parse(strings.TrimSpace(raw))
    if err != nil {
        return err
    }
    if u.Scheme != "http" && u.Scheme != "https" {
        return fmt.Errorf("scheme %q not permitted", u.Scheme)
    }
    return nil
}

Try / catch

err := proxy.Do(c, upstream)
if errors.Is(err, proxy.ErrUpstreamSchemeNotAllowed) {
    return fiber.NewError(fiber.StatusBadRequest, "upstream scheme not allowed")
}

Prevention

When it happens

Trigger: Proxied target uses a scheme like file://, ftp://, gopher://, or an empty scheme, none of which are in the allowlist enforced at security.go:330.

Common situations: Attacker-controlled upstream allowing file:// to read local files; misconfigured upstream with a wrong scheme; intentional use of a non-http scheme that must be explicitly allowed via SecurityPolicy.AllowedSchemes.

Related errors


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