caddyserver/caddy · error

destination must be a placeholder and only a placeholder

Error message

destination must be a placeholder and only a placeholder

What it means

The `map` handler (http.handlers.map) requires each entry of `destinations` to be exactly one placeholder token such as {http.vars.my_var}. Provision rejects a destination whose '{' count is not exactly 1 or that does not start with '{', because the trimmed name is later used as a replacer variable key.

Source

Thrown at modules/caddyhttp/map/map.go:68

	// If no mappings match or if the mapped output is null/nil, the associated
	// default output will be applied (optional).
	Defaults []string `json:"defaults,omitempty"`
}

// CaddyModule returns the Caddy module information.
func (Handler) CaddyModule() caddy.ModuleInfo {
	return caddy.ModuleInfo{
		ID:  "http.handlers.map",
		New: func() caddy.Module { return new(Handler) },
	}
}

// Provision sets up h.
func (h *Handler) Provision(_ caddy.Context) error {
	for j, dest := range h.Destinations {
		if strings.Count(dest, "{") != 1 || !strings.HasPrefix(dest, "{") {
			return fmt.Errorf("destination must be a placeholder and only a placeholder")
		}
		h.Destinations[j] = strings.Trim(dest, "{}")
	}

	for i, m := range h.Mappings {
		if m.InputRegexp == "" {
			continue
		}
		var err error
		h.Mappings[i].re, err = regexp.Compile(m.InputRegexp)
		if err != nil {
			return fmt.Errorf("compiling regexp for mapping %d: %v", i, err)
		}
	}

	// TODO: improve efficiency even further by using an actual map type
	// for the non-regexp mappings, OR sort them and do a binary search

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Wrap the destination in a single placeholder: {http.vars.my_var}.
  2. Remove any extra braces or literals from the destination entry.
  3. One destination per output; do not concatenate two placeholders into one entry.

Example fix

// before (Caddyfile)
map {http.request.host} {dest} {
  example.com foo
}

// after
map {http.request.host} {http.vars.dest} {
  example.com foo
}
Defensive patterns

Strategy: validation

Validate before calling

import "regexp"

var destRE = regexp.MustCompile(`^\{[^{}]+\}$`)

func validDestinations(dests []string) bool {
	for _, d := range dests {
		if !destRE.MatchString(d) {
			return false
		}
	}
	return true
}

Prevention

When it happens

Trigger: JSON/Caddyfile map config with destinations: ["my_var"], ["{a}{b}"], ["literal"], or a placeholder missing its closing brace like "{http.vars.foo".

Common situations: Assuming destinations are plain variable names (common when moving from rewrite/set patterns); nesting placeholders; leftover braces from templating; adapting an nginx map block where the variable has no braces.

Related errors


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