{"id":"8457f7c10b001c44","repo":"gofiber/fiber","slug":"errupstreamschemenotallowed","errorCode":"ErrUpstreamSchemeNotAllowed","errorMessage":"%w: %q","messagePattern":"%w: %q","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"middleware/proxy/security.go","lineNumber":331,"sourceCode":"\t\treturn u, nil\n\t}\n\tif err := validateHostForSSRF(u.Hostname()); err != nil {\n\t\treturn nil, err\n\t}\n\treturn u, nil\n}\n\n// parseUpstreamScheme parses raw and enforces the scheme allowlist and\n// host presence without performing any DNS resolution. url.Parse can\n// leave Host set while Hostname() is empty (e.g. \"http://:8080\"), so the\n// presence check uses Hostname.\nfunc parseUpstreamScheme(raw string, policy SecurityPolicy) (*url.URL, error) {\n\tu, err := parseUpstream(raw)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif !schemeAllowed(u.Scheme, policy.AllowedSchemes) {\n\t\treturn nil, fmt.Errorf(\"%w: %q\", ErrUpstreamSchemeNotAllowed, u.Scheme)\n\t}\n\tif u.Hostname() == \"\" {\n\t\treturn nil, ErrUpstreamHostInvalid\n\t}\n\treturn u, nil\n}\n\n// validateUpstreamForBalancer validates a statically configured Balancer\n// upstream. It enforces the scheme allowlist and rejects IP-literal hosts\n// in blocked ranges, but defers hostname resolution to the SSRF-guarded\n// dialer (see newSSRFDialer). Deferring DNS keeps a transient resolver\n// failure at startup from panicking the application (e.g. crash loops in\n// container orchestrators) and re-checks the resolved IP on every dial,\n// which also defeats DNS-rebinding.\nfunc validateUpstreamForBalancer(raw string, policy SecurityPolicy) (*url.URL, error) {\n\tu, err := parseUpstreamScheme(raw, policy)\n\tif err != nil {\n\t\treturn nil, err","sourceCodeStart":313,"sourceCodeEnd":349,"githubUrl":"https://github.com/gofiber/fiber/blob/9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c/middleware/proxy/security.go#L313-L349","documentation":"Returned by parseUpstreamScheme when the upstream URL's scheme is not in the configured AllowedSchemes allowlist (default: http, https). Wraps the ErrUpstreamSchemeNotAllowed sentinel.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure the upstream uses http or https.","If a different scheme is genuinely required, explicitly add it via SecurityPolicy.AllowedSchemes (review the security implications first).","Reject user-controlled upstream input that specifies a scheme other than http/https."],"exampleFix":"// before: user-supplied upstream may use file://\nproxy.Do(ctx, userInput)\n\n// after: force http(s) only\nu, _ := url.Parse(userInput)\nif u.Scheme != \"http\" && u.Scheme != \"https\" {\n    return fiber.NewError(fiber.StatusBadRequest, \"scheme not allowed\")\n}\nproxy.Do(ctx, u.String())","handlingStrategy":"validation","validationCode":"// Reject any scheme other than http/https before proxying.\nfunc allowedScheme(raw string) error {\n    u, err := url.Parse(strings.TrimSpace(raw))\n    if err != nil {\n        return err\n    }\n    if u.Scheme != \"http\" && u.Scheme != \"https\" {\n        return fmt.Errorf(\"scheme %q not permitted\", u.Scheme)\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"err := proxy.Do(c, upstream)\nif errors.Is(err, proxy.ErrUpstreamSchemeNotAllowed) {\n    return fiber.NewError(fiber.StatusBadRequest, \"upstream scheme not allowed\")\n}","preventionTips":["Maintain an allowlist of permitted upstream hosts, not just schemes.","Never feed user-controlled URLs directly to the proxy.","Review any decision to widen AllowedSchemes for SSRF impact."],"tags":["proxy","ssrf","scheme","security","allowlist"],"analyzedSha":"9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c","analyzedAt":"2026-08-04T21:44:03.395Z","schemaVersion":2}