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
- Validate the target URL scheme against {http, https} in the handler before calling proxy.Do/Forward/etc.
- If a non-default scheme is legitimately needed, populate SecurityPolicy.AllowedSchemes with the exact allowed set and pass the policy via Config.SecurityPolicy.
- Reject user-controlled target URLs at the trust boundary; never pass raw user input to proxy helpers.
- 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
- Never pass raw user input as the proxy target; parse and allowlist the scheme first.
- Document the project's allowed schemes in CLAUDE.md/AGENTS.md exposure posture.
- Treat any proxy endpoint as public and SSRF-prone until proven otherwise.
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 theView on GitHub (pinned to a105acad6c)