{"record":{"id":"42bd3ffda95cb568","repo":"caddyserver/caddy","slug":"certificate-lifetime-s-should-be-less-than-inte","errorCode":null,"errorMessage":"certificate lifetime (%s) should be less than intermediate certificate lifetime (%s)","messagePattern":"certificate lifetime \\((.+?)\\) should be less than intermediate certificate lifetime \\((.+?)\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddypki/acmeserver/acmeserver.go","lineNumber":160,"sourceCode":"\t}\n\n\tash.warnIfPolicyAllowsAll()\n\n\t// get a reference to the configured CA\n\tappModule, err := ctx.App(\"pki\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tpkiApp := appModule.(*caddypki.PKI)\n\tca, err := pkiApp.GetCA(ctx, ash.CA)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// make sure leaf cert lifetime is less than the intermediate cert lifetime. this check only\n\t// applies for caddy-managed intermediate certificates\n\tif ca.Intermediate == nil && ash.Lifetime >= ca.IntermediateLifetime {\n\t\treturn fmt.Errorf(\"certificate lifetime (%s) should be less than intermediate certificate lifetime (%s)\", time.Duration(ash.Lifetime), time.Duration(ca.IntermediateLifetime))\n\t}\n\n\tdatabase, err := ash.openDatabase()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tauthorityConfig := caddypki.AuthorityConfig{\n\t\tSignWithRoot: ash.SignWithRoot,\n\t\tAuthConfig: &authority.AuthConfig{\n\t\t\tProvisioners: provisioner.List{\n\t\t\t\t&provisioner.ACME{\n\t\t\t\t\tName:       ash.CA,\n\t\t\t\t\tChallenges: ash.Challenges.toSmallstepType(),\n\t\t\t\t\tOptions: &provisioner.Options{\n\t\t\t\t\t\tX509: ash.Policy.normalizeRules(),\n\t\t\t\t\t},\n\t\t\t\t\tType: provisioner.TypeACME.String(),","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddypki/acmeserver/acmeserver.go#L142-L178","documentation":"Enforced by the internal ACME server (smallstep ACME CA) at provisioning: a certificate authority's leaf `lifetime` must be strictly shorter than the intermediate certificate's lifetime. It only applies when Caddy manages the intermediate itself (ca.Intermediate == nil, i.e. no external intermediate cert was configured); with an externally supplied intermediate the check is skipped. Default intermediate lifetime is 7 days (defaultIntermediateLifetime = 24h*7 in ca.go).","triggerScenarios":"Configuring the ACME server handler with `lifetime 168h` (or any value &gt;= 7d = 168h) while relying on Caddy's own intermediate certificate: `if ca.Intermediate == nil && ash.Lifetime &gt;= ca.IntermediateLifetime` triggers. Also with intermediate_lifetime set low and lifetime not lowered to match.","commonSituations":"Raising certificate lifetime on an internal ACME server for device/iot clients that cache certs for weeks; setting lifetime to 720h for a private CA without realizing the stock intermediate lives 7 days; mixing lifetime/intermediate_lifetime units (Caddyfile durations are d/h/m/s).","solutions":["Lower the handler's `lifetime` below the intermediate's lifetime — e.g. `lifetime 12h` (default 12h works with the 7d default intermediate)","Or raise the CA's intermediate lifetime first: set `intermediate_lifetime` on the CA (e.g. 720h) in the pki app config, then the leaf lifetime can go up","Or provide your own external intermediate certificate to the CA (via `intermediate` key/cert), which bypasses this check","Verify by validating config: `caddy validate --config Caddyfile`"],"exampleFix":"# before\n{\n  acme_server {\n    lifetime 720h\n  }\n}\n\n# after (option A: shorter leaf)\n{\n  acme_server {\n    lifetime 24h\n  }\n}\n# after (option B: longer intermediate, pki app JSON)\n\"pki\": { \"certificate_authorities\": { \"local\": { \"intermediate_lifetime\": 2160 } } }","handlingStrategy":"validation","validationCode":"// Check lifetimes before loading acme_server config:\nconst defaultIntermediate = 7 * 24 * time.Hour\nif intermediateLifetime == 0 { intermediateLifetime = defaultIntermediate }\nif externalIntermediate == nil && leafLifetime >= intermediateLifetime {\n    return fmt.Errorf(\"leaf %s must be &lt; intermediate %s\", leafLifetime, intermediateLifetime)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Document the 7-day default intermediate lifetime next to any `lifetime` setting","When raising intermediate_lifetime, audit all acme_server handlers using that CA","Run `caddy validate` after any lifetime changes"],"tags":["pki","acme","certificates","lifetime","config"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}