caddyserver/caddy · error

header-up %d: invalid format "%s" (expecting "Field: value")

Error message

header-up %d: invalid format "%s" (expecting "Field: value")

What it means

The `caddy reverse-proxy` command validates each `--header-up` value as `"Field: value"`. The string is split on the first colon with `strings.Cut`; if no colon is found, or either side is empty after trimming whitespace, Caddy exits with this error naming the offending index and raw string.

Source

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

	}

	handler := Handler{
		TransportRaw: caddyconfig.JSONModuleObject(ht, "protocol", "http", nil),
		Upstreams:    upstreamPool,
	}

	// set up header_up
	headerUp, err := fs.GetStringArray("header-up")
	if err != nil {
		return caddy.ExitCodeFailedStartup, fmt.Errorf("invalid header flag: %v", err)
	}
	if len(headerUp) > 0 {
		reqHdr := make(http.Header)
		for i, h := range headerUp {
			key, val, found := strings.Cut(h, ":")
			key, val = strings.TrimSpace(key), strings.TrimSpace(val)
			if !found || key == "" || val == "" {
				return caddy.ExitCodeFailedStartup, fmt.Errorf("header-up %d: invalid format \"%s\" (expecting \"Field: value\")", i, h)
			}
			reqHdr.Set(key, val)
		}
		handler.Headers = &headers.Handler{
			Request: &headers.HeaderOps{
				Set: reqHdr,
			},
		}
	}

	// set up header_down
	headerDown, err := fs.GetStringArray("header-down")
	if err != nil {
		return caddy.ExitCodeFailedStartup, fmt.Errorf("invalid header flag: %v", err)
	}
	if len(headerDown) > 0 {
		respHdr := make(http.Header)
		for i, h := range headerDown {

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Rewrite the flag value as "Field: value" with both sides non-empty
  2. If the value comes from an environment variable, verify it is set before launching the command
  3. Check the index reported in the message (0-based) against the Nth --header-up argument to find the bad one

Example fix

# before
caddy reverse-proxy --to localhost:9000 --header-up "X-Forwarded-Proto"
# after
caddy reverse-proxy --to localhost:9000 --header-up "X-Forwarded-Proto: https"
Defensive patterns

Strategy: validation

Validate before calling

func validHeaderFlag(s string) bool {
	key, val, found := strings.Cut(s, ":")
	return found && strings.TrimSpace(key) != "" && strings.TrimSpace(val) != ""
}

for i, h := range headerUps {
	if !validHeaderFlag(h) {
		log.Fatalf("header-up %d invalid: %q", i, h)
	}
}

Prevention

When it happens

Trigger: Passing `--header-up "X-Foo"` (no colon), `--header-up ": value"` (empty field name), `--header-up "X-Foo: "` (empty value), or `--header-up "X-Foo:"` (empty value after trim).

Common situations: Forgetting the colon separator, adding a trailing colon with no value, or a shell variable that expands to an empty string, e.g. `--header-up "X-Api-Key: $KEY"` when $KEY is unset in the environment.

Related errors


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