caddyserver/caddy · error · caddy.APIError
invalid Content-Type: %v
Error message
invalid Content-Type: %v
What it means
Returned with HTTP 400 when the request's Content-Type header cannot be parsed by Go's mime.ParseMediaType on /load or /adapt. The header must be a valid MIME type with optional parameters, e.g. 'text/caddyfile; charset=utf-8'. Garbage like 'caddyfile' alone, empty type with parameters (';charset=utf-8'), or stray characters cause the parse to fail.
Source
Thrown at caddyconfig/load.go:189
}
w.Header().Set("Content-Type", "application/json")
return json.NewEncoder(w).Encode(out)
}
// adaptByContentType adapts body to Caddy JSON using the adapter specified by contentType.
// If contentType is empty or ends with "/json", the input will be returned, as a no-op.
func adaptByContentType(contentType string, body []byte) ([]byte, []Warning, error) {
// assume JSON as the default
if contentType == "" {
return body, nil, nil
}
ct, _, err := mime.ParseMediaType(contentType)
if err != nil {
return nil, nil, caddy.APIError{
HTTPStatus: http.StatusBadRequest,
Err: fmt.Errorf("invalid Content-Type: %v", err),
}
}
// if already JSON, no need to adapt
if strings.HasSuffix(ct, "/json") {
return body, nil, nil
}
// adapter name should be suffix of MIME type
_, adapterName, slashFound := strings.Cut(ct, "/")
if !slashFound {
return nil, nil, fmt.Errorf("malformed Content-Type")
}
cfgAdapter := GetAdapter(adapterName)
if cfgAdapter == nil {
return nil, nil, fmt.Errorf("unrecognized config adapter '%s'", adapterName)
}View on GitHub (pinned to 50e54ee279)
Solutions
- Use a well-formed MIME type: Content-Type: text/caddyfile (the part after the slash names the adapter)
- Omit Content-Type entirely if the body is already Caddy JSON (it defaults to JSON)
- End JSON types with /json, e.g. application/json, to skip adaptation
Example fix
# before curl -X POST http://localhost:2019/load -H 'Content-Type: caddyfile' -d @Caddyfile # after curl -X POST http://localhost:2019/load -H 'Content-Type: text/caddyfile' -d @Caddyfile
Defensive patterns
Strategy: validation
Validate before calling
// validate the header before sending
func validCT(ct string) bool {
if ct == "" { return true } // JSON default
t, _, err := mime.ParseMediaType(ct)
return err == nil && strings.Contains(t, "/")
}
if !validCT(ct) { return fmt.Errorf("bad Content-Type %q; use e.g. text/caddyfile", ct) } Try / catch
if resp.StatusCode == http.StatusBadRequest && strings.Contains(bodyText, "invalid Content-Type") {
// fix header client-side; retrying unchanged will always fail
req.Header.Set("Content-Type", "application/json")
} Prevention
- Only use mime.ParseMediaType-legal values: type/subtype with optional ;key=value params
- Keep adapter names in the subtype (text/caddyfile), never as the whole header
When it happens
Trigger: Setting Content-Type: caddyfile (no slash, no type/subtype structure); Content-Type with malformed parameters like 'text/caddyfile;;'; duplicated headers producing an invalid joined value; clients sending 'Content-Type: ' with trailing junk.
Common situations: Scripts that set the adapter name directly as the Content-Type instead of text/<adapter>; copy-paste of header values with smart quotes or whitespace; HTTP libraries auto-appending broken charset parameters.
Related errors
- malformed Content-Type
- unrecognized config adapter '%s'
- reading request body: %v
- loading config: %v
- adapting config using %s adapter: %v
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/9650220223711b03.
Report an issue: GitHub.