caddyserver/caddy · error

invalid TLS renegotiation level: %v

Error message

invalid TLS renegotiation level: %v

What it means

While building a tls.Config for a CA pool source, the configured `renegotiation` value did not match any of the allowed levels. Only "never" (or empty), "once", and "freely" map to tls.RenegotiateNever, tls.RenegotiateOnceAsClient, and tls.RenegotiateFreelyAsClient respectively; anything else is rejected.

Source

Thrown at modules/caddytls/capools.go:617

	if t.CARaw != nil {
		caRaw, err := ctx.LoadModule(t, "CARaw")
		if err != nil {
			return nil, err
		}
		ca := caRaw.(CA)
		cfg.RootCAs = ca.CertPool()
	}

	// Renegotiation
	switch t.Renegotiation {
	case "never", "":
		cfg.Renegotiation = tls.RenegotiateNever
	case "once":
		cfg.Renegotiation = tls.RenegotiateOnceAsClient
	case "freely":
		cfg.Renegotiation = tls.RenegotiateFreelyAsClient
	default:
		return nil, fmt.Errorf("invalid TLS renegotiation level: %v", t.Renegotiation)
	}

	// override for the server name used verify the TLS handshake
	cfg.ServerName = repl.ReplaceKnown(cfg.ServerName, "")

	// throw all security out the window
	cfg.InsecureSkipVerify = t.InsecureSkipVerify

	// only return a config if it's not empty
	if reflect.DeepEqual(cfg, new(tls.Config)) {
		return nil, nil
	}

	return cfg, nil
}

// The HTTPCertPool fetches the trusted root certificates from HTTP(S)
// endpoints. The TLS connection properties can be customized, including custom

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Set the value to exactly one of: never, once, freely (or omit it — default is never).
  2. Check for stray whitespace or case differences in the config value.
  3. Run `caddy validate --config Caddyfile` to catch the typo before reload.

Example fix

# before
trust_pool http https://ca.example.com/bundle.pem {
  renegotiation always
}

# after
trust_pool http https://ca.example.com/bundle.pem {
  renegotiation freely
}
Defensive patterns

Strategy: validation

Validate before calling

// in config generation, whitelist renegotiation values
var validRenegotiation = map[string]bool{"": true, "never": true, "once": true, "freely": true}

func sanitizeRenegotiation(v string) (string, error) {
	if !validRenegotiation[v] {
		return "", fmt.Errorf("invalid renegotiation %q: must be never, once, or freely", v)
	}
	return v, nil
}

Prevention

When it happens

Trigger: Setting `renegotiation <value>` on a trust_pool/tls config where value is e.g. "always", "true", "Never" (case matters), or a typo like "freelly".

Common situations: Porting configs from other servers that use different renegotiation vocabularies; case or spelling mistakes; assuming boolean-like values are accepted.

Understand the failure class

Related errors


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