gofiber/fiber · error

proxy: upstream scheme is not allowed

Error message

proxy: upstream scheme is not allowed

What it means

The proxy middleware rejects any upstream URL whose scheme is not in SecurityPolicy.AllowedSchemes (defaults to {http, https} per defaultAllowedSchemes). This is a primary SSRF defense: it prevents the proxy from being abused to fetch via file://, gopher://, ftp://, or other dangerous schemes. The error is returned by proxy.Do/Forward/DoRedirects/DoTimeout/DoDeadline whenever the resolved target URL's scheme is outside the allowlist.

Solutions

  1. Validate the target URL scheme against {http, https} in the handler before calling proxy.Do/Forward/etc.
  2. If a non-default scheme is legitimately needed, populate SecurityPolicy.AllowedSchemes with the exact allowed set and pass the policy via Config.SecurityPolicy.
  3. Reject user-controlled target URLs at the trust boundary; never pass raw user input to proxy helpers.
  4. Log the offending scheme to detect probing/abuse attempts.

Example fix

// before
app.Get("/proxy", func(c fiber.Ctx) error {
    return proxy.Do(c, c.Query("url"))
})

// after
app.Get("/proxy", func(c fiber.Ctx) error {
    u, err := url.Parse(c.Query("url"))
    if err != nil || (u.Scheme != "http" && u.Scheme != "https") {
        return fiber.NewError(fiber.StatusBadRequest, "invalid target URL")
    }
    return proxy.Do(c, u.String())
})
Defensive patterns

Strategy: validation

Validate before calling

u, err := url.Parse(target)
if err != nil {
    return fiber.NewError(fiber.StatusBadRequest, "invalid target URL")
}
allowed := map[string]bool{"http": true, "https": true}
if cfg, ok := policy.(*proxy.SecurityPolicy); ok && len(cfg.AllowedSchemes) > 0 {
    allowed = make(map[string]bool, len(cfg.AllowedSchemes))
    for _, s := range cfg.AllowedSchemes { allowed[s] = true }
}
if !allowed[u.Scheme] {
    return fiber.NewError(fiber.StatusBadRequest, "scheme not permitted")
}

Type guard

func isAllowedScheme(u *url.URL, allowed []string) bool {
    set := map[string]bool{"http": true, "https": true}
    for _, s := range allowed { set[s] = true }
    return set[u.Scheme]
}

Prevention

When it happens

Trigger: Calling proxy.Do(c, targetURL) where targetURL has a scheme other than http/https (e.g., file:///etc/passwd, gopher://, ftp://). Also triggered if you set SecurityPolicy.AllowedSchemes to a custom list and the target uses a scheme not in that list.

Common situations: Handlers that proxy user-supplied URLs without validation; misconfigured upstream targets with wrong scheme typos; SSRF attack payloads hitting a public proxy endpoint; switching from http to a custom protocol without updating AllowedSchemes.

Related errors


AI-assisted analysis of gofiber/fiber@a105acad6c (2026-08-11). Data as JSON: /api/errors/f4b72a331b69c84d. Report an issue: GitHub.

Appendix: source

Thrown at middleware/proxy/security.go:51

// defaultAllowedSchemes is the internal, read-only allowlist used as the
// fallback inside schemeAllowed when a policy carries no AllowedSchemes.
// It is never handed out by reference: DefaultSecurityPolicy() and
// normalizePolicy() copy it before it can reach the exported
// 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

View on GitHub (pinned to a105acad6c)