Skip to content
Back to Blog
SSL & Security10 min read

Advanced Free SSL Alternatives to Let's Encrypt: 8 Moves

Push beyond default ACME workflows with DNS validation tricks, multi-CDN failover, OCSP stapling tuning, and other expert-level SSL optimizations most setups ignore.

Written by Abdul AbrorTechnical Hosting Support Engineer
Advanced Free SSL Alternatives to Let's Encrypt: 8 Moves
On this page

You've installed certbot, run the plugin, renewed a few times—fine. That handles the first mile. What I'm seeing in production environments is that the default playbook leaves performance and resilience on the table, especially when you're managing dozens of domains, serving traffic through multiple CDNs, or dealing with firewalled infrastructure that can't expose port 80.

This isn't a beginner's guide to ACME clients. We're talking about the stuff that matters after you've outgrown the five-minute Quick Start.

1. Switch to DNS-01 challenges and automate the record lifecycle

HTTP-01 validation requires an open port 80 and a web server that can serve the challenge token. That's a non-starter if your origin is behind a firewall, you're using a fully proxied CDN setup, or you're issuing wildcards.

DNS-01 puts a TXT record in your zone instead. Most ACME clients support it, but the trap is manual record creation. Automate it with your DNS provider's API—Cloudflare, Route 53, and most others expose endpoints for programmatic updates.

With certbot, the pattern looks like this:

certbot certonly --dns-cloudflare \
  --dns-cloudflare-credentials ~/.secrets/cloudflare.ini \
  -d example.com -d *.example.com

The credentials file holds your API token. Store it mode 600, owned by root. Set up a deploy hook to reload your web server or push the cert to a CDN edge.

Wildcard certificates cover an entire subdomain tree with one issuance, cutting API calls and renewal churn. You can't do that with HTTP validation.

2. Run dual issuers for failover

Let's Encrypt has rate limits: 50 certificates per registered domain per week, 300 new orders per account every three hours. If you're managing a multi-tenant platform or a domain reseller service, you'll hit those.

ZeroSSL and Buypass offer ACME endpoints with different rate structures. Set up a second ACME client pointed at ZeroSSL's directory URL and script a fallback: if certbot renewal fails, trigger the alternate issuer.

I've run this pattern in environments where a brief Let's Encrypt outage or a rate-limit spike would have bricked certificate renewals for hundreds of domains. The overhead is minimal—just another cron job and a different --server flag.

certbot certonly --server https://acme.zerossl.com/v2/DV90 \
  --email [email protected] --agree-tos \
  --dns-cloudflare --dns-cloudflare-credentials ~/.secrets/cloudflare.ini \
  -d backup-domain.com

Browsers don't care which free CA signed the cert. Your users won't notice. You just bought redundancy.

3. Enable and tune OCSP stapling

OCSP stapling eliminates a round-trip to the CA's OCSP responder during the TLS handshake. The server fetches the revocation status itself and attaches it to the certificate during negotiation.

For nginx, add these directives inside your server block:

ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
resolver 1.1.1.1 1.0.0.1 valid=300s;
resolver_timeout 5s;

The ssl_trusted_certificate must point to the issuer chain, not the leaf cert. Use a fast public resolver or your own recursive resolver to avoid lookup delays.

Apache requires mod_ssl with SSLUseStapling and SSLStaplingCache defined globally:

SSLUseStapling on
SSLStaplingCache shmcb:/var/run/ocsp(128000)

Verify it's working:

openssl s_client -connect example.com:443 -status -tlsextdebug < /dev/null 2>&1 | grep -A 17 'OCSP response:'

You should see OCSP Response Status: successful. If you see no response sent, check your resolver config and firewall rules for outbound UDP/53 and TCP/80 to the OCSP endpoint.

In support tickets I handled, the usual culprit was a missing chain file or a resolver that couldn't reach the internet.

4. Parallelize renewal across multi-server deployments

Running certbot on every web node creates race conditions and wastes API calls. Centralize certificate issuance on a dedicated renewal host, then distribute the PEM files.

One approach: issue certs on a single control node, store them in a shared secrets manager (Vault, AWS Secrets Manager, or even an S3 bucket with strict IAM), and pull them during deployment or via a cron job on each app server.

Another: use a post-renewal hook to rsync the live directory to your web nodes:

#!/bin/bash
# /etc/letsencrypt/renewal-hooks/deploy/sync-certs.sh
rsync -avz --delete /etc/letsencrypt/live/ user@web01:/etc/letsencrypt/live/
rsync -avz --delete /etc/letsencrypt/live/ user@web02:/etc/letsencrypt/live/
ssh user@web01 'systemctl reload nginx'
ssh user@web02 'systemctl reload nginx'

Set up SSH keys for passwordless access and restrict the remote command to just the reload operation.

If you're behind a load balancer, you can also terminate TLS at the balancer and manage a single certificate there. HAProxy, Nginx as a reverse proxy, and cloud load balancers all support this.

5. Pin intermediate certificates and monitor for changes

Let's Encrypt rotates intermediate CAs periodically. If you've hardcoded the intermediate fingerprint in your app's certificate pinning logic, a rotation will break your clients.

Instead of pinning the leaf or intermediate, pin the root CA public key or include multiple backup pins. Extract the public key hash:

openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -pubkey -noout | \
  openssl pkey -pubin -outform der | \
  openssl dgst -sha256 -binary | base64

Store at least two pins in your HTTP Public Key Pinning header or your mobile app's trust store: one for the current cert and one for a backup issuer or the root.

Monitor the chain.pem file for changes. If the intermediate switches, you'll get advance notice before clients start rejecting connections.

So what if you're serving from multiple CDNs?

Cloudflare, Fastly, and other edge networks can provision their own certificates, but if you want to use your own origin cert across all of them, you'll need to upload it to each platform.

Use the provider's API or CLI. For Cloudflare:

curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/custom_certificates" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{"certificate":"CERT_PEM","private_key":"KEY_PEM"}'

Automate this in your renewal hook. Each CDN has its own upload endpoint and certificate format requirements—some want the full chain in one file, others want leaf and chain separate.

If one CDN goes down, DNS failover can route traffic to another edge. But if your certificate isn't pre-loaded on both, users will see a cert error during the failover window.

6. Compress and cache the certificate chain

TLS 1.3 allows certificate compression using Brotli or zstd. Not all clients support it yet, but for those that do, you can shave a few kilobytes off the handshake.

Nginx doesn't expose a direct config for this, but you can precompress the chain and serve it conditionally if you're using a custom TLS library or a CDN that supports it.

More practical: ensure your chain includes only the intermediate and root required for validation. Some ACME clients append redundant cross-signed intermediates. Let's Encrypt's current chain is short—just one intermediate—but older deployments sometimes carried legacy cross-signs.

Check what's in the chain:

openssl crl2pkcs7 -nocrl -certfile /etc/letsencrypt/live/example.com/chain.pem | \
  openssl pkcs7 -print_certs -noout

You should see exactly one issuer certificate. If you see two or more, trim the extras.

7. Rotate private keys on a schedule

Most renewal workflows reuse the same private key indefinitely. That's fine for casual sites. For anything handling sensitive data or under compliance scrutiny, rotate the key every renewal cycle.

Certbot's --reuse-key flag is on by default. To force a new key:

certbot renew --force-renewal --no-reuse-key

Rotating keys limits the exposure window if a key is ever compromised. Pair this with short certificate lifetimes—Let's Encrypt issues 90-day certs, which is already better than the old 1-2 year standard, but you can renew even more frequently if you automate it.

Store old private keys in an encrypted archive for forensic purposes, but never serve them again.

8. Monitor expiry across all domains with centralized telemetry

When you're managing tens or hundreds of certificates, manual checks don't scale. Script expiry monitoring and feed it into your observability stack.

A simple cron job can parse the certificate on each domain and alert if expiry is within a threshold:

#!/bin/bash
for domain in $(cat /etc/domains.txt); do
  expiry=$(echo | openssl s_client -servername $domain -connect $domain:443 2>/dev/null | \
    openssl x509 -noout -enddate | cut -d= -f2)
  expiry_epoch=$(date -d "$expiry" +%s)
  now=$(date +%s)
  days_left=$(( ($expiry_epoch - $now) / 86400 ))
  if [ $days_left -lt 30 ]; then
    echo "$domain expires in $days_left days" | mail -s "Cert expiry warning" [email protected]
  fi
done

For more sophisticated setups, export expiry timestamps as Prometheus metrics or CloudWatch custom metrics and graph them in Grafana. Set alerts for thresholds like 21 days, 14 days, and 7 days.

I've seen production outages triggered by a single forgotten subdomain whose cert expired silently. Monitoring catches that before users do.

What logs and endpoints to check first

If renewals fail, start with /var/log/letsencrypt/letsencrypt.log. Most errors are DNS propagation delays, firewall blocks, or rate limits.

For DNS-01 challenges, verify the TXT record actually exists:

dig _acme-challenge.example.com TXT +short

If you see nothing, the API call didn't complete or the zone hasn't propagated. Check your DNS provider's control panel and confirm the API token has write permissions.

For HTTP-01, test the challenge path manually:

curl -I http://example.com/.well-known/acme-challenge/test

You should get a 404 or 403, not a redirect to HTTPS or a proxy error. If you're behind Cloudflare or another proxy, make sure it's not caching or transforming the challenge response.

OCSP stapling failures usually show up in your web server's error log. For nginx, look for OCSP_basic_verify() failed or no OCSP responder URL. For Apache, check ssl_error_log.

Check your resolver can actually reach the OCSP endpoint:

curl -I http://ocsp.letsencrypt.org/

If that times out, it's a network or firewall issue, not a config problem.

FAQ

Can I use Let's Encrypt for internal services not exposed to the internet?
Yes, with DNS-01 validation. Your DNS zone must be public, but the service itself can be private.

Do I need to restart the web server after every renewal?
Reload is usually enough. Nginx and Apache both support graceful reloads that don't drop active connections.

What's the difference between ZeroSSL and Let's Encrypt rate limits?
ZeroSSL has different thresholds and structures, but exact numbers aren't published. Test both and monitor which hits limits first in your use case.

Can I automate multi-CDN certificate uploads?
Yes. Use each CDN's API in a post-renewal hook. Store API credentials securely and test the upload path before relying on it.

How do I test OCSP stapling without a live connection?
Use openssl s_client -connect with the -status flag against your server. Look for the OCSP Response Status field in the output.

Where the real gains come from

Default ACME workflows get you encrypted traffic. These eight moves get you resilience, speed, and operational sanity at scale. DNS challenges eliminate firewall headaches. Dual issuers protect against CA outages. OCSP stapling shaves milliseconds off every handshake. Centralized monitoring catches expiry before it becomes an incident.

Pick the two or three that map to your actual pain points and implement them this week. The tooling is already there.