gofiber/fiber · error
ErrUpstreamSchemeNotAllowed
ErrUpstreamSchemeNotAllowed
Error message
proxy: upstream scheme is not allowed
What it means
proxy.Balancer validates each entry in Config.Servers via validateUpstreamForBalancer, which calls parseUpstreamScheme and enforces a scheme allowlist. If the URL's scheme is not allowed (default allowlist is http/https), it returns ErrUpstreamSchemeNotAllowed wrapped as fmt.Errorf("%w: %q", ErrUpstreamSchemeNotAllowed, scheme); Balancer then panics with that error. This is an SSRF-defense measure: non-HTTP schemes (gopher, file, etc.) must never be proxied.
Solutions
- Use http:// or https:// schemes for all Servers entries.
- If you legitimately need another scheme, configure a SecurityPolicy with an AllowedSchemes list that includes it (and review the SSRF implications).
- Ensure every server string has an explicit scheme prefix — reconstruct with fmt.Sprintf("http://%s", host) if needed.
Example fix
// before
app.Use(proxy.Balancer(proxy.Config{
Servers: []string{"gopher://cache:6379"},
}))
// after
app.Use(proxy.Balancer(proxy.Config{
Servers: []string{"http://cache:6379"},
})) Defensive patterns
Strategy: validation
Validate before calling
var allowedSchemes = map[string]struct{}{"http": {}, "https": {}}
func validateUpstreamSchemes(servers []string) error {
for _, s := range servers {
u, err := url.Parse(s)
if err != nil {
return fmt.Errorf("bad upstream %q: %w", s, err)
}
if _, ok := allowedSchemes[u.Scheme]; !ok {
return fmt.Errorf("%w: %q", proxy.ErrUpstreamSchemeNotAllowed, u.Scheme)
}
}
return nil
} Type guard
func isAllowedUpstreamScheme(scheme string) bool {
_, ok := map[string]struct{}{"http": {}, "https": {}}[strings.ToLower(scheme)]
return ok
} Prevention
- Restrict upstream schemes to http/https unless a reviewed SecurityPolicy explicitly widens them.
- Always prefix server strings with an explicit scheme.
- Validate the scheme allowlist at config load to surface SSRF-risky URLs before startup.
When it happens
Trigger: A Servers entry like "gopher://internal:6379", "file:///etc/passwd", "ftp://host", or an entry missing a scheme (which parses with Scheme="" and is rejected). Also triggered by a custom SecurityPolicy whose AllowedSchemes omits the scheme you actually use.
Common situations: Pointing the balancer at a non-HTTP backend by mistake; a misconfigured custom SecurityPolicy that narrows AllowedSchemes below what your upstreams need; a server string that lost its scheme during templating ("upstream:8080" instead of "http://upstream:8080").
Related errors
- ErrUpstreamHostBlocked
- proxy: upstream scheme is not allowed
- %w: %q
- ErrUpstreamHostBlocked
- ErrUpstreamHostInvalid
AI-assisted analysis of gofiber/fiber@a105acad6c (2026-08-11).
Data as JSON: /api/errors/332fedb494d62074.
Report an issue: GitHub.
Appendix: source
Thrown at middleware/proxy/proxy.go:41
// Balancer creates a load balancer among multiple upstream servers
func Balancer(config ...Config) fiber.Handler {
// Set default config
cfg := configDefault(config...)
policy := resolvePolicy(cfg.SecurityPolicy)
// Load balanced client
lbc := &fasthttp.LBClient{}
// Note that Servers, Timeout, WriteBufferSize, ReadBufferSize and TLSConfig
// will not be used if the client are set.
if cfg.Client == nil {
// Set timeout
lbc.Timeout = cfg.Timeout
// Validate each upstream against the configured policy and build
// a HostClient per server.
for _, server := range cfg.Servers {
u, err := validateUpstreamForBalancer(server, policy)
if err != nil {
panic(err)
}
client := &fasthttp.HostClient{
NoDefaultUserAgentHeader: true,
DisablePathNormalizing: true,
Addr: u.Host,
MaxConns: cfg.MaxConnsPerHost,
ReadBufferSize: cfg.ReadBufferSize,
WriteBufferSize: cfg.WriteBufferSize,
TLSConfig: secureTLSConfig(cfg.TLSConfig),
DialDualStack: cfg.DialDualStack,
MaxResponseBodySize: cfg.MaxResponseBodySize,
}
if u.Scheme == schemeHTTPS {View on GitHub (pinned to a105acad6c)