caddyserver/caddy · error

the 'forwarded' option is no longer supported; use the 'clie

Error message

the 'forwarded' option is no longer supported; use the 'client_ip' matcher instead

What it means

Before publishing ECH configs, Caddy serializes the config list to its binary DNS wire format via echCfgList.MarshalBinary (the crypto/tls EncryptedClientHelloConfigList encoding). Marshal failed, meaning one of the in-memory ECH configs is malformed for wire encoding (bad config bytes, unsupported version, or an empty list edge case).

Source

Thrown at modules/caddyhttp/ip_matchers.go:121

		// name of the macro, this is the function name that users see when writing expressions.
		"remote_ip",
		// name of the function that the macro will be rewritten to call.
		"remote_ip_match_request_list",
		// internal data type of the MatchPath value.
		[]*cel.Type{cel.ListType(cel.StringType)},
		// function to convert a constant list of strings to a MatchPath instance.
		func(data ref.Val) (RequestMatcherWithError, error) {
			refStringList := stringSliceType
			strList, err := data.ConvertToNative(refStringList)
			if err != nil {
				return nil, err
			}

			m := MatchRemoteIP{}

			for _, input := range strList.([]string) {
				if input == "forwarded" {
					return nil, errors.New("the 'forwarded' option is no longer supported; use the 'client_ip' matcher instead")
				}
				m.Ranges = append(m.Ranges, input)
			}

			err = m.Provision(ctx)
			return m, err
		},
	)
}

// Provision parses m's IP ranges, either from IP or CIDR expressions.
func (m *MatchRemoteIP) Provision(ctx caddy.Context) error {
	m.logger = ctx.Logger()
	cidrs, zones, err := provisionCidrsZonesFromRanges(m.Ranges)
	if err != nil {
		return err
	}
	m.cidrs = cidrs

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Unwrap and inspect the MarshalBinary error for the offending config (often indicates a specific record length/version problem).
  2. Stop Caddy and reset the ech/configs storage folder so fresh, well-formed configs are generated, then let publication repopulate DNS records.
  3. After Caddy version upgrades with ECH enabled, expect rotation to replace old configs; if marshal fails immediately post-upgrade, reset state.
  4. Verify no external tooling writes into Caddy's storage.

Example fix

# before: publication fails on corrupt stored config
# after: regenerate ECH state
systemctl stop caddy && rm -rf /var/lib/caddy/ech/configs && systemctl start caddy
Defensive patterns

Strategy: fallback

Try / catch

On marshal failure of the config list, fall back to resetting ECH state: stop Caddy, back up and remove ech/configs, restart — fresh configs marshal cleanly and republish to DNS.

Prevention

When it happens

Trigger: tls.EncryptedClientHelloConfigList built from stored configs fails MarshalBinary: a config.bin entry loaded from storage is truncated/corrupt in a way that survived the load cleanup path, or a version/length field is inconsistent.

Common situations: Storage corruption of config.bin; a Caddy upgrade changing the expected binary layout while old configs remain in storage; hand-edited storage.

Related errors


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