{"record":{"id":"bdd973759348af31","repo":"caddyserver/caddy","slug":"client-certificate-file-specified-without-client-c","errorCode":null,"errorMessage":"client_certificate_file specified without client_certificate_key_file","messagePattern":"client_certificate_file specified without client_certificate_key_file","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddyhttp/reverseproxy/httptransport.go","lineNumber":795,"sourceCode":"\t// attempted even if it is configured. Handy when using dynamic upstreams that\n\t// return HTTP and HTTPS endpoints too.\n\t// When specified, TLS will automatically be configured on the transport.\n\t// The value can be a list of any valid tcp port numbers, default empty.\n\tExceptPorts []string `json:\"except_ports,omitempty\"`\n\n\t// The list of elliptic curves to support. Caddy's\n\t// defaults are modern and secure.\n\tCurves []string `json:\"curves,omitempty\"`\n}\n\n// MakeTLSClientConfig returns a tls.Config usable by a client to a backend.\n// If there is no custom TLS configuration, a nil config may be returned.\nfunc (t *TLSConfig) MakeTLSClientConfig(ctx caddy.Context) (*tls.Config, error) {\n\tcfg := new(tls.Config)\n\n\t// client auth\n\tif t.ClientCertificateFile != \"\" && t.ClientCertificateKeyFile == \"\" {\n\t\treturn nil, fmt.Errorf(\"client_certificate_file specified without client_certificate_key_file\")\n\t}\n\tif t.ClientCertificateFile == \"\" && t.ClientCertificateKeyFile != \"\" {\n\t\treturn nil, fmt.Errorf(\"client_certificate_key_file specified without client_certificate_file\")\n\t}\n\tif t.ClientCertificateFile != \"\" && t.ClientCertificateKeyFile != \"\" {\n\t\tcert, err := tls.LoadX509KeyPair(t.ClientCertificateFile, t.ClientCertificateKeyFile)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"loading client certificate key pair: %v\", err)\n\t\t}\n\t\tcfg.Certificates = []tls.Certificate{cert}\n\t}\n\tif t.ClientCertificateAutomate != \"\" {\n\t\t// TODO: use or enable ctx.IdentityCredentials() ...\n\t\ttlsAppIface, err := ctx.App(\"tls\")\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"getting tls app: %v\", err)\n\t\t}\n\t\ttlsApp := tlsAppIface.(*caddytls.TLS)","sourceCodeStart":777,"sourceCodeEnd":813,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddyhttp/reverseproxy/httptransport.go#L777-L813","documentation":"In the transport's tls block, client_certificate_file and client_certificate_key_file are a required pair for mTLS to the upstream. MakeTLSClientConfig validates the pairing first and returns this error when a cert file is set but the key file is empty, so no tls.Config is produced and provisioning fails.","triggerScenarios":"transport http { tls { client_certificate_file /etc/certs/client.pem } } with no client_certificate_key_file; JSON \"client_certificate_file\" set while \"client_certificate_key_file\" is omitted or empty string.","commonSituations":"Copy-pasting half of an mTLS example; assuming the key lives beside the cert and will be inferred; JSON generated from a template where the key field is conditionally omitted; renaming fields during config migration so one name resolves empty.","solutions":["Add the matching client_certificate_key_file pointing at the PEM/TLS key that pairs with the certificate.","Verify both paths exist and are readable before reload (caddy validate --config).","If you actually want an automated certificate, remove client_certificate_file and use client_certificate_automate instead (different mechanism, no key file needed)."],"exampleFix":"// before (Caddyfile)\ntransport http {\n    tls {\n        client_certificate_file /etc/certs/client.pem\n    }\n}\n\n// after\ntransport http {\n    tls {\n        client_certificate_file /etc/certs/client.pem\n        client_certificate_key_file /etc/certs/client.key\n    }\n}","handlingStrategy":"validation","validationCode":"func validateClientCertPair(certFile, keyFile string) error {\n    if certFile != \"\" && keyFile == \"\" {\n        return fmt.Errorf(\"client_certificate_file specified without client_certificate_key_file\")\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always author cert and key directives as an adjacent pair in templates.","Include both fields or neither in generated JSON.","Run caddy validate in CI for every config artifact."],"tags":["config","mtls","tls","reverseproxy","validation"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}