caddyserver/caddy · error

%s: entry %q does not contain a %q

Error message

%s: entry %q does not contain a %q

What it means

Each header alias allowlist entry (after removing a trailing '*') must contain at least one separator character — '_' or '.', depending on the directive — because the alias mechanism works by rewriting that separator to/from '-' in header names. Entries without a separator are rejected at provisioning with the required separator shown via %q.

Source

Thrown at modules/caddyhttp/server.go:411

			if entry[i] >= 0x80 {
				return nil, nil, nil, fmt.Errorf("%s: entry %q contains non-ASCII characters", directive, entry)
			}
		}

		isGlob := strings.HasSuffix(entry, "*")
		name := entry
		if isGlob {
			name = strings.TrimSuffix(entry, "*")
		}

		// Reject entries with '*' not at the trailing position.
		if strings.ContainsRune(name, '*') {
			return nil, nil, nil, fmt.Errorf("%s: entry %q has '*' in an invalid position (only a trailing '*' is allowed)", directive, entry)
		}

		// The name (without trailing '*') must contain at least one separator.
		if !strings.ContainsRune(name, sep) {
			return nil, nil, nil, fmt.Errorf("%s: entry %q does not contain a %q", directive, entry, sep)
		}

		canonAllow := http.CanonicalHeaderKey(name)
		canonDrop := http.CanonicalHeaderKey(strings.ReplaceAll(name, string(sep), "-"))

		if isGlob {
			prefixRules = append(prefixRules, aliasPrefixRule{
				allow: canonAllow,
				drop:  canonDrop,
			})
		} else {
			exactAllow[canonAllow] = struct{}{}
			exactDrop[canonDrop] = struct{}{}
		}
	}

	return exactAllow, exactDrop, prefixRules, nil
}

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Add the required separator to the header name so an alias actually exists
  2. Remove entries that name headers without separators — they cannot be aliased
  3. Check the %q in the message to confirm which separator ('_' or '.') the directive expects

Example fix

// before
"-": ["XApiKey"]

// after
"-": ["X_Api_Key"]
Defensive patterns

Strategy: validation

Validate before calling

func hasSeparator(entries []string, sep rune) error {
    for _, e := range entries {
        name := strings.TrimSuffix(e, "*")
        if !strings.ContainsRune(name, sep) {
            return fmt.Errorf("entry %q lacks required %q separator", e, sep)
        }
    }
    return nil
}

Prevention

When it happens

Trigger: An entry like "XApiKey" or "xapi" with no '_' or '.'; only names such as "X_Api_Key" or "x.api.key" are meaningful aliases.

Common situations: Listing ordinary header names (no separator) in the alias allowlist, misunderstanding that the feature targets underscore/dot-to-dash canonicalization.

Related errors


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