ZeroSSL #
ZeroSSL is a commercial Certificate Authority (CA) offering free and paid TLS certificates using the open standard ACME protocol. Originally known as a traditional SSL service provider, ZeroSSL is now operated by apilayer and backed by Sectigo’s infrastructure (formerly Comodo CA). One of the major breakthroughs in the modern web server world happened when Caddy v2.4 introduced ZeroSSL as its automatic Certificate Authority fallback. This integration made Caddy the first web server to implement built-in CA redundancy out of the box.
Understanding ZeroSSL and how to configure it gives you very high operational flexibility. In enterprise production environments demanding 100% reliability, relying on a single CA is a single point of failure risk. By understanding ZeroSSL’s architecture and how Caddy uses it for risk mitigation, you can design HTTPS infrastructure that is far more resilient to network disruptions, CA outages, and rate limit constraints.
Comparative Analysis: ZeroSSL vs Let’s Encrypt #
Before deciding to switch to ZeroSSL as your primary CA, it’s important to understand the technical and non-technical differences between ZeroSSL and Let’s Encrypt at the free tier:
| Analysis Criteria | Let’s Encrypt | ZeroSSL (Free Tier via ACME) |
|---|---|---|
| Organization Model | Nonprofit (ISRG) backed by industry sponsors. | Commercial (apilayer / Sectigo). |
| Free Certificate Limit | Unlimited. | Unlimited if created via the ACME protocol. (Limited to 3 if created manually through the web dashboard). |
| Rate Limits | Strict (50 certificates per registered domain per week). | Much looser / not strictly published. |
| EAB Requirement (ACME) | No EAB (External Account Binding) needed. | Requires EAB if you want certificates registered to your personal dashboard account. |
| Root Trust Chain | ISRG Root X1 (Pure Let’s Encrypt ownership). | AAA Certificate Services / USERTrust (Sectigo). |
| IP Address Certificates | Doesn’t support public IP address certificates via ACME. | Supports certificates for public IP addresses via ACME. |
| Web Monitoring Dashboard | No built-in dashboard. | Provides a full web dashboard for certificate management. |
| Cryptography Support | Full RSA and ECDSA key support. | Supports RSA and ECDSA keys. |
| SLA & Technical Support | None (relies on community forums). | Available commercially for paid tier customers. |
The Importance of a Multi-CA Redundancy Strategy #
One classic problem in web infrastructure operations is dependency on a single service provider. Although Let’s Encrypt has outstanding reliability, occasional outages still happen. When Let’s Encrypt experiences technical issues or database problems, traditional web servers configured only with Certbot can’t issue or renew certificates. This can cause certificates to expire mid-stream and break user access.
Caddy solves this by pioneering an automatic Multi-CA Redundancy strategy:
flowchart TD
A["Caddy Submits a Certificate Request"] --> B["Contact Let's Encrypt (Primary CA)"]
B --> C{"Does Let's Encrypt Respond & Succeed?"}
C -- Yes --> D["Install Let's Encrypt Certificate & Done"]
C -- No --> E["Log Warning & Switch to Fallback"]
E --> F["Contact ZeroSSL (Fallback CA)"]
F --> G{"Does ZeroSSL Respond & Succeed?"}
G -- Yes --> H["Install ZeroSSL Certificate & Done"]
G -- No --> I["Log Critical Error & Schedule Retry with Backoff"]Behind the scenes, if Let’s Encrypt replies with an error code (like connection issues, a busy server, or if you’ve hit the rate limit), Caddy’s TLS engine immediately detects the failure. Caddy then switches to contacting ZeroSSL’s ACME directory using a secondary ACME account. This entire switching process runs automatically without any extra configuration from you — and most importantly, without causing service downtime.
The External Account Binding (EAB) Concept #
External Account Binding (EAB) is defined in RFC 8555 Section 7.3.4 as a mechanism for binding a new ACME account to an existing customer account on a commercial CA server. With Let’s Encrypt, anyone can create an ACME account anonymously with just an email address. However, because ZeroSSL is a commercial entity, they need to ensure every certificate issued via free ACME is tied to a registered user account on their web dashboard.
Technically, the EAB process involves cryptographic signing using the HMAC algorithm (usually HMAC-SHA256). When Caddy creates a new account at ZeroSSL, it must include an HMAC signature made with the EAB HMAC Key provided by ZeroSSL, which ZeroSSL verifies using your EAB Key ID. This HMAC security guarantees that no one else can claim certificates for your domain on their ZeroSSL account without holding a valid HMAC Key.
Configuring ZeroSSL as the Primary Certificate Authority #
If you want to use ZeroSSL as the primary CA (for example, to leverage their monitoring dashboard in your corporate environment), you must explicitly register an account and configure External Account Binding (EAB).
Step 1: Getting EAB Credentials in the ZeroSSL Dashboard #
- Register and log in to your account at app.zerossl.com.
- In the left sidebar, click the Developer menu.
- In the ACME Credentials section, click the Generate button.
- The system displays two important key strings:
- EAB Key ID (e.g.,
abCDEfg12345678) - EAB HMAC Key (e.g.,
xyz123abc456...)
- EAB Key ID (e.g.,
- Copy both strings to use in the Caddy configuration.
Alternative: Creating EAB Credentials via REST API #
If you want to automate EAB credential creation without opening the web dashboard, ZeroSSL provides a REST API:
# Create new EAB credentials using curl
curl -s -X POST "https://api.zerossl.com/acme/eab-credentials?access_key=YOUR_ACCOUNT_API_KEY" \
-H "Content-Type: application/json"
# Successful JSON output:
# {
# "success": true,
# "eab_kid": "abCDEfg12345678",
# "eab_hmac_key": "xyz123abc456..."
# }
Step 2: Configuring the Caddyfile with EAB #
Once you have the EAB credentials, you must write them in the Caddyfile global options block so Caddy can authenticate to your ZeroSSL account when requesting certificates:
# Global options to set ZeroSSL as the primary CA
{
email [email protected]
cert_issuer acme {
# ZeroSSL ACME directory endpoint
ca https://acme.zerossl.com/v2/DV90
# EAB (External Account Binding) configuration
eab {
key_id "your-eab-key-id-here"
mac_key "your-eab-hmac-key-here"
}
}
}
example.com {
reverse_proxy localhost:8080
}
Certificate Issuance for Public IP Addresses #
One of ZeroSSL’s main advantages over Let’s Encrypt is support for TLS certificates on public IP addresses. Let’s Encrypt currently refuses to issue certificates for raw IPs via ACME. If you have an API service that must be accessed directly using a public IP address (for example, https://203.0.113.50), ZeroSSL is the right solution.
Verification for public IP addresses is done using the HTTP-01 challenge on port 80. Here’s an example Caddyfile configuration for a public IP using ZeroSSL:
# Securing a public IP address using ZeroSSL EAB
203.0.113.50 {
tls {
issuer acme {
ca https://acme.zerossl.com/v2/DV90
eab {
key_id {env.ZEROSSL_KEY_ID}
mac_key {env.ZEROSSL_MAC_KEY}
}
}
}
# Regular response handler
respond "Hello from the Encrypted Public IP!" 200
}
Best Practice: Using Environment Variables for EAB Credentials #
Following system security guidelines, you must not hardcode secret credentials directly in the Caddyfile. Caddyfiles are often stored in Git repositories, and putting EAB keys there is a major security risk. The right way is to use environment variables.
Caddy supports environment variable interpolation using the {env.VARIABLE_NAME} syntax:
# Using environment variables for EAB credentials
{
email {env.ACME_EMAIL}
cert_issuer acme {
ca https://acme.zerossl.com/v2/DV90
eab {
key_id {env.ZEROSSL_KEY_ID}
mac_key {env.ZEROSSL_MAC_KEY}
}
}
}
example.com {
file_server
}
How to Inject Environment Variables: #
1. On a Systemd Service (Linux) #
Use the systemd override feature to inject variables without modifying the built-in unit file:
# Open Caddy's systemd override editor
sudo systemctl edit caddy
Add the following lines inside the service block:
[Service]
Environment="[email protected]"
Environment="ZEROSSL_KEY_ID=your_key_id_here"
Environment="ZEROSSL_MAC_KEY=your_hmac_key_here"
Save the file, then reload the daemon and restart Caddy:
sudo systemctl daemon-reload
sudo systemctl restart caddy
2. On Docker Compose #
If Caddy runs inside a Docker container, include the variables in the environment section of the docker-compose.yml file:
version: "3.7"
services:
caddy:
image: caddy:2.8.4
ports:
- "80:80"
- "443:443"
environment:
- [email protected]
- ZEROSSL_KEY_ID=your_key_id_here
- ZEROSSL_MAC_KEY=your_hmac_key_here
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
volumes:
caddy_data:
caddy_config:
Explicit Custom Multi-CA Configuration #
If you want to keep Let’s Encrypt as the primary CA but bind the ZeroSSL fallback to your personal EAB account (so fallback certificates still appear in your ZeroSSL dashboard), you can declare both certificate issuers in sequence in the global options block:
# Custom configuration with two explicitly registered CAs
{
email [email protected]
# Issuer 1: Let's Encrypt (Primary CA)
cert_issuer acme {
ca https://acme-v02.api.letsencrypt.org/directory
}
# Issuer 2: ZeroSSL with EAB (Fallback CA)
cert_issuer acme {
ca https://acme.zerossl.com/v2/DV90
eab {
key_id {env.ZEROSSL_KEY_ID}
mac_key {env.ZEROSSL_MAC_KEY}
}
}
}
example.com {
reverse_proxy localhost:3000
}
With the configuration above, Caddy always tries Let’s Encrypt first using an anonymous Let’s Encrypt account. If Let’s Encrypt fails, Caddy switches to ZeroSSL, connected to your ZeroSSL dashboard account via EAB.
ZeroSSL Dashboard Features and Certificate Auditing #
One of the main motivations for companies switching to ZeroSSL is the web administration dashboard. Let’s Encrypt doesn’t provide a visual dashboard; you can only track issued certificates through public query sites like crt.sh.
The ZeroSSL dashboard provides various visual management features:
1. Active Certificate List:
View all domain names currently using your ZeroSSL certificates.
2. Expiry Alerts:
The dashboard gives clear visual indicators for certificates approaching
their expiration date (for example, 15 days remaining).
3. Revocation Management:
You can revoke a certificate directly from the web dashboard with one click
if a private key leak happens on the server.
4. SLA & Commercial Support:
If you subscribe to a paid tier, you get uptime guarantees, technical support
via email/ticket, and longer-duration certificates (up to 1 year).
ZeroSSL Troubleshooting Guide #
When configuring ZeroSSL with Caddy, a few EAB- and CA-specific issues often appear. Here’s how to handle them:
1. EAB Authentication Failure (External Account Binding Failed) #
- Log Symptoms: The Caddy log shows errors like
registration failed: acme: error: 400 ... JWS verification errororinvalid EAB credentials. - Cause: The Key ID or HMAC Key strings copied from the ZeroSSL dashboard are wrong, expired, or have already been used by another ACME client before.
- Solution: Reopen the ZeroSSL dashboard, click the button to generate new ACME Credentials, then carefully recopy the keys into your server’s environment variable file.
2. Email Address Mismatch #
- Log Symptoms: The registration process fails with an email-related error message.
- Cause: The email address in the
emailoption of the Caddyfile differs from the email registered on the ZeroSSL account owning the EAB credentials. - Solution: Make sure the global
emailparameter in the Caddyfile is exactly the same as your ZeroSSL dashboard login email.
3. Certificates Still Use Let’s Encrypt #
- Symptom: You’ve configured ZeroSSL as the primary CA in the Caddyfile, but when you check via browser or
openssl, the installed certificate is still issued by Let’s Encrypt. - Cause: Caddy won’t replace an existing certificate while its validity is still active and safe (> 30 days). Caddy uses the certificate file in local storage until the next renewal schedule arrives.
- Solution: If you want to migrate to ZeroSSL right now without waiting for the renewal period, you must manually delete the old Let’s Encrypt certificate from Caddy’s storage:
# Find and delete the old Let's Encrypt certificate files for the relevant domain
sudo find /var/lib/caddy/.local/share/caddy/ -name "*example.com*" -type d -exec rm -rf {} +
# Reload the Caddy configuration so it requests a new certificate from ZeroSSL
sudo systemctl reload caddy
Summary #
- ZeroSSL — A commercial Certificate Authority backed by Sectigo’s infrastructure, fully compatible with the ACME protocol, and offering unlimited free options via ACME.
- Automatic CA Redundancy — Caddy by default positions ZeroSSL as the backup CA. If Let’s Encrypt has issues, Caddy automatically switches to ZeroSSL without your intervention.
- External Account Binding (EAB) — Special credentials that must be generated in the ZeroSSL dashboard and installed in Caddy so certificates connect to your personal web monitoring dashboard.
- IP Address Certificates — ZeroSSL’s advantage over Let’s Encrypt is its ability to issue SSL certificates for raw public IP addresses via the HTTP-01 challenge.
- Credential Security — Always use environment variables (
{env.ZEROSSL_KEY_ID}) to store EAB keys so they aren’t exposed in the Caddyfile in Git.- Management Dashboard — The benefit of using ZeroSSL is the visual dashboard for monitoring status, tracking validity, and revoking certificates if a private key leaks.