{"id":"47335b7579cfd67b","repo":"gofiber/fiber","slug":"proxy-parse-upstream-q-w","errorCode":null,"errorMessage":"proxy: parse upstream %q: %w","messagePattern":"proxy: parse upstream %q: %w","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"middleware/proxy/security.go","lineNumber":297,"sourceCode":"\t\t\t}\n\t\t}\n\t}\n}\n\n// parseUpstream returns the parsed url.URL for raw. Hosts without an\n// explicit scheme default to http:// to match the historical Balancer\n// behavior where bare \"host:port\" entries were accepted.\nfunc parseUpstream(raw string) (*url.URL, error) {\n\traw = utils.TrimSpace(raw)\n\tif raw == \"\" {\n\t\treturn nil, ErrUpstreamHostInvalid\n\t}\n\tif !strings.Contains(raw, \"://\") {\n\t\traw = \"http://\" + raw\n\t}\n\tu, err := url.Parse(raw)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"proxy: parse upstream %q: %w\", raw, err)\n\t}\n\treturn u, nil\n}\n\n// validateUpstream parses raw, enforces the scheme allowlist, and unless\n// the policy permits private addresses, resolves the hostname and\n// rejects responses that include any blocked address. Rejecting on a\n// single blocked answer mitigates DNS rebinding attempts in which the\n// resolver returns a mix of public and private IPs.\nfunc validateUpstream(raw string, policy SecurityPolicy) (*url.URL, error) {\n\tu, err := parseUpstreamScheme(raw, policy)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif policy.AllowPrivateIPs {\n\t\treturn u, nil\n\t}\n\tif err := validateHostForSSRF(u.Hostname()); err != nil {","sourceCodeStart":279,"sourceCodeEnd":315,"githubUrl":"https://github.com/gofiber/fiber/blob/9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c/middleware/proxy/security.go#L279-L315","documentation":"Returned by parseUpstream when url.Parse fails on the raw upstream string. Bare hosts without a scheme are first prefixed with http://, so this fires only when even that constructed URL is syntactically invalid.","triggerScenarios":"Passing a malformed upstream to proxy.Do/Forward/Balancer, e.g. a string with illegal control characters, an invalid percent-encoding, or a scheme/authority that url.Parse rejects.","commonSituations":"User-controlled or config-supplied upstream URL with stray characters; unescaped spaces/control bytes; a typo like 'http//host'; copy-paste of a URL with backslashes or raw unicode.","solutions":["Validate and sanitize the upstream string with net/url.Parse before configuring the proxy.","Trim whitespace and reject strings with control characters.","Construct upstream URLs from trusted components (scheme + host + port) rather than concatenating raw input."],"exampleFix":"// before: passing raw config straight through\nproxy.Do(ctx, cfg.UpstreamURL) // may be malformed\n\n// after: pre-validate\nu, err := url.Parse(strings.TrimSpace(cfg.UpstreamURL))\nif err != nil {\n    return fiber.NewError(fiber.StatusBadRequest, \"bad upstream\")\n}\nproxy.Do(ctx, u.String())","handlingStrategy":"validation","validationCode":"// Validate the upstream URL parses before handing it to the proxy.\nfunc validUpstream(raw string) (string, error) {\n    raw = strings.TrimSpace(raw)\n    if !strings.Contains(raw, \"://\") {\n        raw = \"http://\" + raw\n    }\n    u, err := url.Parse(raw)\n    if err != nil {\n        return \"\", err\n    }\n    return u.String(), nil\n}","typeGuard":null,"tryCatchPattern":"if _, err := url.Parse(upstream); err != nil {\n    return fiber.NewError(fiber.StatusBadRequest,\n        \"upstream URL is malformed\")\n}\nreturn proxy.Do(c, upstream)","preventionTips":["Always url.Parse upstream strings sourced from config or users.","Reject strings containing control bytes or whitespace.","Construct upstreams from trusted scheme+host+port components."],"tags":["proxy","url-parsing","configuration","input-validation"],"analyzedSha":"9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c","analyzedAt":"2026-08-04T21:44:03.395Z","schemaVersion":2}