caddyserver/caddy · error

invalid upstream address %s: %v

Error message

invalid upstream address %s: %v

What it means

One of the --to upstream values failed parseUpstreamDialAddress while the command was normalizing upstream addresses. This is the CLI equivalent of the Caddyfile upstream parse errors: scheme+placeholder conflicts, bad ports, paths in URLs, or scheme/port conflicts, surfaced under 'invalid upstream address %s'.

Source

Thrown at modules/caddyhttp/reverseproxy/command.go:140

		}
	}
	if fromAddr.Port == "" {
		switch fromAddr.Scheme {
		case "http":
			fromAddr.Port = httpPort
		case "https":
			fromAddr.Port = httpsPort
		}
	}

	// set up the upstream address; assume missing information from given parts
	// mixing schemes isn't supported, so use first defined (if available)
	toAddresses := make([]string, len(to))
	var toScheme string
	for i, toLoc := range to {
		addr, err := parseUpstreamDialAddress(toLoc)
		if err != nil {
			return caddy.ExitCodeFailedStartup, fmt.Errorf("invalid upstream address %s: %v", toLoc, err)
		}
		if addr.scheme != "" && toScheme == "" {
			toScheme = addr.scheme
		}
		toAddresses[i] = addr.dialAddr()
	}

	// proceed to build the handler and server
	ht := HTTPTransport{}
	if toScheme == "https" {
		ht.TLS = new(TLSConfig)
		if insecure {
			ht.TLS.InsecureSkipVerify = true
		}
	}

	upstreamPool := UpstreamPool{}
	for _, toAddr := range toAddresses {

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Fix the named upstream per the underlying error: host[:port] or scheme://host[:port] only.
  2. Move path rewriting into a Caddyfile (rewrite + reverse_proxy) instead of the quick command.
  3. Re-run with a minimal known-good --to like 'localhost:8080' to confirm the rest of the command works.

Example fix

# before
caddy reverse-proxy --from example.com --to http://backend:9000/api

# after
caddy reverse-proxy --from example.com --to http://backend:9000
Defensive patterns

Strategy: validation

Validate before calling

for _, t := range toValues {
    if strings.Contains(t, "://") {
        if strings.Contains(t, "{") {
            return fmt.Errorf("--to %q: placeholders not allowed with a scheme", t)
        }
        if u, err := url.Parse(t); err != nil || u.Path != "" || u.RawQuery != "" || u.Fragment != "" {
            return fmt.Errorf("--to %q must be scheme://host[:port] only", t)
        }
    }
}

Prevention

When it happens

Trigger: 'caddy reverse-proxy --from x --to https://{env.H}', '--to http://host/path', '--to http://host:443', or any dial-address syntax violation from the parseUpstreamDialAddress rules.

Common situations: First-time users pasting full backend URLs with paths; env-var-driven deployments attempting placeholders in the scheme-bearing address.

Related errors


AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15). Data as JSON: /api/errors/715a50430bc47c6d. Report an issue: GitHub.