caddyserver/caddy · error
your email address is required to use ZeroSSL's ACME endpoin
Error message
your email address is required to use ZeroSSL's ACME endpoint
What it means
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.
Source
Thrown at modules/caddytls/acmeissuer.go:366
}
// GetACMEIssuer returns iss. This is useful when other types embed ACMEIssuer, because
// type-asserting them to *ACMEIssuer will fail, but type-asserting them to an interface
// with only this method will succeed, and will still allow the embedded ACMEIssuer
// to be accessed and manipulated.
func (iss *ACMEIssuer) GetACMEIssuer() *ACMEIssuer { return iss }
// GetRenewalInfo wraps the underlying GetRenewalInfo method and satisfies
// the CertMagic interface for ARI support.
func (iss *ACMEIssuer) GetRenewalInfo(ctx context.Context, cert certmagic.Certificate) (acme.RenewalInfo, error) {
return iss.issuer.GetRenewalInfo(ctx, cert)
}
// generateZeroSSLEABCredentials generates ZeroSSL EAB credentials for the primary contact email
// on the issuer. It should only be usedif the CA endpoint is ZeroSSL. An email address is required.
func (iss *ACMEIssuer) generateZeroSSLEABCredentials(ctx context.Context, acct acme.Account) (*acme.EAB, acme.Account, error) {
if strings.TrimSpace(iss.Email) == "" {
return nil, acme.Account{}, fmt.Errorf("your email address is required to use ZeroSSL's ACME endpoint")
}
if len(acct.Contact) == 0 {
// we borrow the email from config or the default email, so ensure it's saved with the account
acct.Contact = []string{"mailto:" + iss.Email}
}
endpoint := zerossl.BaseURL + "/acme/eab-credentials-email"
form := url.Values{"email": []string{iss.Email}}
body := strings.NewReader(form.Encode())
req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, body)
if err != nil {
return nil, acct, fmt.Errorf("forming request: %v", err)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("User-Agent", certmagic.UserAgent)
View on GitHub (pinned to 50e54ee279)
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.
Example fix
# before
{
# no email configured
}
example.com {
tls {
issuer zerossl
}
}
# after
{
email you@example.com
}
example.com {
tls {
issuer zerossl
}
} Defensive patterns
Strategy: validation
Validate before calling
// Validate config before provisioning a ZeroSSL issuer.
if strings.Contains(strings.ToLower(caURL), "zerossl") && strings.TrimSpace(email) == "" {
return errors.New("ZeroSSL requires an email: set the global email or issuer email")
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- failed getting EAB credentials: HTTP %d: %s (code %d)
- expanding email address '%s': %v
- DNS challenge enabled, but no DNS provider configured
- forming request: %v
- performing EAB credentials request: %v
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/fd16dd46a07229f6.
Report an issue: GitHub.