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

  1. 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.
  2. If using environment placeholders like {$ACME_EMAIL}, ensure the variable is actually set and non-empty in the environment Caddy runs in.
  3. 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

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


AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15). Data as JSON: /api/errors/fd16dd46a07229f6. Report an issue: GitHub.