caddyserver/caddy · error
parsing upstream URL: parse "%v": port range invalid: %v
Error message
parsing upstream URL: parse "%v": port range invalid: %v
What it means
url.Parse failed with an 'invalid port' error containing '-', so Caddy assumed a port range and replaced everything after the last ':' with a dummy port — but the extracted port range does not contain exactly one '-' (strings.Count(portRange, "-") != 1). Only a single contiguous range like '8080-8090' is supported.
Source
Thrown at modules/caddyhttp/reverseproxy/addresses.go:88
// so we return a more user-friendly error message instead
// to explain what to do instead
if strings.Contains(upstreamAddr, "{") {
return parsedAddr{}, fmt.Errorf("due to parsing difficulties, placeholders are not allowed when an upstream address contains a scheme")
}
toURL, err := url.Parse(upstreamAddr)
if err != nil {
// if the error seems to be due to a port range,
// try to replace the port range with a dummy
// single port so that url.Parse() will succeed
if strings.Contains(err.Error(), "invalid port") && strings.Contains(err.Error(), "-") {
index := strings.LastIndex(upstreamAddr, ":")
if index == -1 {
return parsedAddr{}, fmt.Errorf("parsing upstream URL: %v", err)
}
portRange := upstreamAddr[index+1:]
if strings.Count(portRange, "-") != 1 {
return parsedAddr{}, fmt.Errorf("parsing upstream URL: parse \"%v\": port range invalid: %v", upstreamAddr, portRange)
}
toURL, err = url.Parse(strings.ReplaceAll(upstreamAddr, portRange, "0"))
if err != nil {
return parsedAddr{}, fmt.Errorf("parsing upstream URL: %v", err)
}
port = portRange
} else {
return parsedAddr{}, fmt.Errorf("parsing upstream URL: %v", err)
}
}
if port == "" {
port = toURL.Port()
}
// there is currently no way to perform a URL rewrite between choosing
// a backend and proxying to it, so we cannot allow extra components
// in backend URLs
if toURL.Path != "" || toURL.RawQuery != "" || toURL.Fragment != "" {View on GitHub (pinned to 50e54ee279)
Solutions
- Use a single contiguous port range: 'http://host:8080-8090'.
- Split discontiguous ranges into multiple upstreams: 'reverse_proxy host:8080-8090 host:9090'.
- Wrap IPv6 literals in brackets so the last colon is the port separator: 'http://[fe80::1]:8080'.
Example fix
# before reverse_proxy http://backend:8080-8090-8100 # after reverse_proxy http://backend:8080-8090 http://backend:8100
Defensive patterns
Strategy: validation
Validate before calling
func validPortRange(addr string) bool {
i := strings.LastIndex(addr, ":")
if i < 0 || i == len(addr)-1 {
return false
}
pr := addr[i+1:]
return strings.Count(pr, "-") == 1 && pr[0] != '-'
}
for _, u := range upstreams {
if strings.Contains(u, "://") && strings.Contains(u, "-") && !validPortRange(u) {
return fmt.Errorf("upstream %q: only single contiguous port ranges (start-end) allowed", u)
}
} Prevention
- Express discontiguous port sets as multiple upstreams, never multiple dashes.
- Keep port ranges as low-high with low <= high.
- Bracket IPv6 hosts so the last colon is the port separator.
When it happens
Trigger: Upstream addresses such as 'http://host:8080-8090-8100' (two dashes), 'http://host:-8080' (leading dash), or where the last segment after the colon coincidentally contains multiple dashes, e.g. IPv6 or hyphenated hostnames misparsed as ports.
Common situations: Trying to express multiple discontiguous port ranges in one upstream; hyphenated hostnames where the colon detection lands in the wrong place.
Related errors
- due to parsing difficulties, placeholders are not allowed wh
- parsing upstream URL: %v
- for now, URLs for proxy upstreams only support scheme, host,
- parsing upstream '%s': %w
- upstream address has conflicting scheme (http://) and port (
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/43ec7d1a8525f6fa.
Report an issue: GitHub.