{"record":{"id":"fd16dd46a07229f6","repo":"caddyserver/caddy","slug":"your-email-address-is-required-to-use-zerossl-s-ac","errorCode":null,"errorMessage":"your email address is required to use ZeroSSL's ACME endpoint","messagePattern":"your email address is required to use ZeroSSL's ACME endpoint","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddytls/acmeissuer.go","lineNumber":366,"sourceCode":"}\n\n// GetACMEIssuer returns iss. This is useful when other types embed ACMEIssuer, because\n// type-asserting them to *ACMEIssuer will fail, but type-asserting them to an interface\n// with only this method will succeed, and will still allow the embedded ACMEIssuer\n// to be accessed and manipulated.\nfunc (iss *ACMEIssuer) GetACMEIssuer() *ACMEIssuer { return iss }\n\n// GetRenewalInfo wraps the underlying GetRenewalInfo method and satisfies\n// the CertMagic interface for ARI support.\nfunc (iss *ACMEIssuer) GetRenewalInfo(ctx context.Context, cert certmagic.Certificate) (acme.RenewalInfo, error) {\n\treturn iss.issuer.GetRenewalInfo(ctx, cert)\n}\n\n// generateZeroSSLEABCredentials generates ZeroSSL EAB credentials for the primary contact email\n// on the issuer. It should only be usedif the CA endpoint is ZeroSSL. An email address is required.\nfunc (iss *ACMEIssuer) generateZeroSSLEABCredentials(ctx context.Context, acct acme.Account) (*acme.EAB, acme.Account, error) {\n\tif strings.TrimSpace(iss.Email) == \"\" {\n\t\treturn nil, acme.Account{}, fmt.Errorf(\"your email address is required to use ZeroSSL's ACME endpoint\")\n\t}\n\n\tif len(acct.Contact) == 0 {\n\t\t// we borrow the email from config or the default email, so ensure it's saved with the account\n\t\tacct.Contact = []string{\"mailto:\" + iss.Email}\n\t}\n\n\tendpoint := zerossl.BaseURL + \"/acme/eab-credentials-email\"\n\tform := url.Values{\"email\": []string{iss.Email}}\n\tbody := strings.NewReader(form.Encode())\n\n\treq, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, body)\n\tif err != nil {\n\t\treturn nil, acct, fmt.Errorf(\"forming request: %v\", err)\n\t}\n\treq.Header.Set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\treq.Header.Set(\"User-Agent\", certmagic.UserAgent)\n","sourceCodeStart":348,"sourceCodeEnd":384,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddytls/acmeissuer.go#L348-L384","documentation":"generateZeroSSLEABCredentials is called when the issuer's CA endpoint is ZeroSSL and needs to mint External Account Binding credentials via ZeroSSL's API. ZeroSSL's EAB endpoint requires an email address, so Caddy refuses to proceed when iss.Email is empty (after trimming whitespace). The error surfaces during account registration/Provision of an ACME issuer pointed at ZeroSSL.","triggerScenarios":"Setting the ACME issuer's CA URL to ZeroSSL (https://zerossl.com/acme/eab or the default when using ZeroSSL) without an email on the issuer and without a global/default email (acme_email or tls automation email) that would have been inherited.","commonSituations":"Migrating an existing config from Let's Encrypt to ZeroSSL while never having configured an email; setting ca zerossl in the Caddyfile but relying on email being optional as it is with Let's Encrypt; providing an email made only of whitespace via an environment placeholder that expanded to empty.","solutions":["Set an email on the issuer: \"email\": \"you@example.com\" in the ACME issuer JSON, or the email global option / acme_email in the Caddyfile.","If using environment placeholders like {$ACME_EMAIL}, ensure the variable is actually set and non-empty in the environment Caddy runs in.","If you cannot share an email, use ZeroSSL with manually generated EAB credentials (external_account with eab_kid/eab_hmac_key) or switch back to Let's Encrypt, which does not require email."],"exampleFix":"# before\n{\n\t# no email configured\n}\nexample.com {\n\ttls {\n\t\tissuer zerossl\n\t}\n}\n\n# after\n{\n\temail you@example.com\n}\nexample.com {\n\ttls {\n\t\tissuer zerossl\n\t}\n}","handlingStrategy":"validation","validationCode":"// Validate config before provisioning a ZeroSSL issuer.\nif strings.Contains(strings.ToLower(caURL), \"zerossl\") && strings.TrimSpace(email) == \"\" {\n    return errors.New(\"ZeroSSL requires an email: set the global email or issuer email\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always set the global email option in configs that use ZeroSSL.","CI-validate configs with `caddy validate` which exercises provisioning.","Prefer env-var backed emails (email {$ACME_EMAIL}) with the variable guaranteed set in the unit file."],"tags":["tls","acme","zerossl","configuration"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}