caddyserver/caddy · error

replacement for header field

Error message

replacement %d for header field '%s': %v

What it means

Returned during HeaderOps.Provision while precompiling regex replacements: the replacement's search_regexp for a header field failed regexp.Compile. Placeholders like {http.request.uri} inside the regex are skipped (compiled at runtime), but a regex without placeholders must compile now — invalid regex syntax aborts provisioning.

Solutions

  1. Copy the exact regex from the error and test it with Go's regexp syntax, e.g. in a scratch Go program or an online RE2 tester.
  2. Remove PCRE-only constructs: backreferences, lookahead/lookbehind; rewrite using RE2-compatible patterns.
  3. Balance groups and escape literal parentheses/brackets.
  4. Validate the whole config with `caddy validate` after fixing.

Example fix

# before (PCRE backreference, invalid in RE2)
replace Host {
    search_regexp "^(www\.)?(.+)$"
    replace "example.com$1"  # intended \2 semantics
}
# after (RE2-safe capture groups)
replace Host {
    search_regexp "^(?:www\.)?(.+)$"
    replace "cdn.example.com${1}"
}
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight in Go: compile every regex before shipping config
for field, reps := range cfg.Replace {
    for i, r := range reps {
        if r.SearchRegexp == "" { continue }
        if _, err := regexp.Compile(r.SearchRegexp); err != nil {
            return fmt.Errorf("field %s replacement %d: %w", field, i, err)
        }
    }
}

Prevention

When it happens

Trigger: Configuring header > replace with a search_regexp containing invalid RE2 syntax, e.g. unbalanced parentheses, backreferences (\1 — unsupported in RE2), or stray quantifiers; the error message includes the field name, replacement index, and compile error.

Common situations: Porting nginx sub_filter or Apache mod_substitute regexes that use PCRE-only features; forgetting Go uses RE2 (no lookaheads/backreferences); escaping mistakes through JSON config layers.

Related errors


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

Appendix: source

Thrown at modules/caddyhttp/headers/headers.go:154

	if ops == nil {
		return nil // it's possible no ops are configured; fix #6893
	}
	for fieldName, replacements := range ops.Replace {
		for i, r := range replacements {
			if r.SearchRegexp == "" {
				continue
			}

			// Check if it contains placeholders
			if containsPlaceholders(r.SearchRegexp) {
				// Contains placeholders, skips precompilation, and recompiles at runtime
				continue
			}

			// Does not contain placeholders, safe to precompile
			re, err := regexp.Compile(r.SearchRegexp)
			if err != nil {
				return fmt.Errorf("replacement %d for header field '%s': %v", i, fieldName, err)
			}
			replacements[i].re = re
		}
	}
	return nil
}

// containsPlaceholders checks if the string contains Caddy placeholder syntax {key}
func containsPlaceholders(s string) bool {
	_, after, ok := strings.Cut(s, "{")
	if !ok {
		return false
	}
	closeIdx := strings.Index(after, "}")
	if closeIdx == -1 {
		return false
	}
	// Make sure there is content between the brackets

View on GitHub (pinned to 50e54ee279)