gofiber/fiber · error
proxy: parse upstream
Error message
proxy: parse upstream %q: %w
What it means
parseUpstream trims and normalizes the raw upstream string (defaulting to http:// if no scheme) then calls url.Parse. If url.Parse returns an error — typically for control characters, invalid percent-escapes, or malformed URLs — it is wrapped with the normalized raw value for diagnosis. This is the lowest-level parse failure, before scheme/host checks.
Solutions
- Inspect the quoted raw value in the message: it shows exactly what was parsed after trimming and scheme defaulting.
- Re-enter the upstream string as a clean ASCII value with no spaces or control characters.
- Validate upstreams at config load time and fail fast with a clear message rather than at request time.
- If upstreams come from user input, sanitize (reject control bytes, require valid percent-encoding) before passing to proxy.
- URL-encode any path components that legitimately contain special characters.
Example fix
// before: trailing space or control char in env
upstream := os.Getenv("UPSTREAM") // "http://svc:8080\r"
// after: trim and validate
upstream := strings.TrimSpace(os.Getenv("UPSTREAM"))
if _, err := url.Parse(upstream); err != nil { log.Fatalf("bad UPSTREAM: %v", err) } Defensive patterns
Strategy: validation
Validate before calling
// Validate upstream strings at config load.
func validUpstream(raw string) error {
if strings.TrimSpace(raw) == "" { return errors.New("empty upstream") }
if !strings.Contains(raw, "://") { raw = "http://" + raw }
_, err := url.Parse(raw)
return err
} Prevention
- Trim and parse every upstream at config load; fail fast.
- Source upstreams from env with care (no trailing whitespace/quotes).
- Reject control bytes in any user-supplied URL before passing to proxy.
- Use ASCII-only config sources.
When it happens
Trigger: Configured upstream contains raw control bytes (e.g. a CR/LF injection in a config file), invalid percent-encoding like '%zz', unescaped spaces, or a stray bracket. Also triggered by user input forwarded as an upstream target without sanitization.
Common situations: Misformatted Balancer.Servers or proxy.Targets entry in config; env var with trailing whitespace or quotes that survive into url.Parse; a config-management tool that wrote a non-printable character; copy-paste from a rich-text source introducing smart quotes.
Related errors
- proxy: invalid dial address
- Servers cannot be empty
- client: invalid proxy URL
- ErrUpstreamHostBlocked
- ErrUpstreamHostInvalid
AI-assisted analysis of gofiber/fiber@a105acad6c (2026-08-11).
Data as JSON: /api/errors/47335b7579cfd67b.
Report an issue: GitHub.
Appendix: source
Thrown at middleware/proxy/security.go:293
}
}
}
}
// parseUpstream returns the parsed url.URL for raw. Hosts without an
// explicit scheme default to http:// to match the historical Balancer
// behavior where bare "host:port" entries were accepted.
func parseUpstream(raw string) (*url.URL, error) {
raw = utils.TrimSpace(raw)
if raw == "" {
return nil, ErrUpstreamHostInvalid
}
if !strings.Contains(raw, "://") {
raw = "http://" + raw
}
u, err := url.Parse(raw)
if err != nil {
return nil, fmt.Errorf("proxy: parse upstream %q: %w", raw, err)
}
return u, nil
}
// validateUpstream parses raw, enforces the scheme allowlist, and unless
// the policy permits private addresses, resolves the hostname and
// rejects responses that include any blocked address. Rejecting on a
// single blocked answer mitigates DNS rebinding attempts in which the
// resolver returns a mix of public and private IPs.
func validateUpstream(raw string, policy SecurityPolicy) (*url.URL, error) {
u, err := parseUpstreamScheme(raw, policy)
if err != nil {
return nil, err
}
if policy.AllowPrivateIPs {
return u, nil
}
if err := validateHostForSSRF(u.Hostname()); err != nil {View on GitHub (pinned to a105acad6c)