caddyserver/caddy · error
due to parsing difficulties, placeholders are not allowed wh
Error message
due to parsing difficulties, placeholders are not allowed when an upstream address contains a scheme
What it means
parseUpstreamDialAddress rejects upstream addresses that contain both a scheme (detected via '://') and a '{' placeholder character. url.Parse cannot handle Caddy placeholders, so instead of an opaque parse error Caddy fails fast with this explicit message telling you the two features are mutually exclusive.
Source
Thrown at modules/caddyhttp/reverseproxy/addresses.go:73
}
func (p parsedAddr) isUnix() bool {
return caddy.IsUnixNetwork(p.network)
}
// parseUpstreamDialAddress parses configuration inputs for
// the dial address, including support for a scheme in front
// as a shortcut for the port number, and a network type,
// for example 'unix' to dial a unix socket.
func parseUpstreamDialAddress(upstreamAddr string) (parsedAddr, error) {
var network, scheme, host, port string
if strings.Contains(upstreamAddr, "://") {
// we get a parsing error if a placeholder is specified
// 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 {View on GitHub (pinned to 50e54ee279)
Solutions
- Remove the scheme and keep the placeholder, e.g. 'reverse_proxy {env.BACKEND_HOST}:443' (or use transport block with tls for HTTPS semantics).
- If the scheme must vary, resolve the full address (scheme included is still impossible; resolve host+port) outside Caddy via environment variables per deployment.
- Use dynamic upstream modules (e.g. 'dynamic srv' or 'dynamic a') when the backend set must be computed at runtime.
Example fix
# before (Caddyfile)
reverse_proxy https://{env.BACKEND_HOST}
# after
reverse_proxy {env.BACKEND_HOST}:443 {
transport http {
tls
}
} Defensive patterns
Strategy: validation
Validate before calling
func hasSchemePlaceholder(addr string) bool {
return strings.Contains(addr, "://") && strings.Contains(addr, "{")
}
for _, u := range cfg.Upstreams {
if hasSchemePlaceholder(u.Dial) {
return fmt.Errorf("upstream %q mixes a scheme with a placeholder; move the port into the value or use a transport block", u.Dial)
}
} Prevention
- Keep upstream addresses as host[:port] and express TLS via 'transport http { tls }'.
- Resolve env vars to full host:port strings outside the config.
- Assert generated configs contain no '{' in scheme-bearing upstreams before caddy.Run.
When it happens
Trigger: Configuring a reverse_proxy upstream like 'https://{env.BACKEND_HOST}' or 'http://{upstream_host}:8080' where the address string contains both a scheme and any placeholder.
Common situations: Trying to select the backend scheme from environment variables or request-derived placeholders; forgetting that the port can be inferred from the scheme and that placeholders work only in scheme-less upstream addresses.
Related errors
- parsing upstream URL: %v
- parsing upstream URL: parse "%v": port range invalid: %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/a744f2a949014aa6.
Report an issue: GitHub.