Let's Encrypt & ACME Protocol Guide
Complete guide to free SSL/TLS certificates with Let's Encrypt and the ACME protocol
š What is Let's Encrypt?
Let's Encrypt is a free, automated, and open Certificate Authority (CA) that provides SSL/TLS certificates trusted by all major browsers. It uses the ACME (Automatic Certificate Management Environment) protocol to automate certificate issuance and renewal.
Key benefits
- Free: No cost for certificates (domain validation only)
- Automated: ACME protocol enables automatic issuance and renewal
- Trusted: Certificates trusted by 99%+ of browsers
- Secure: Modern security standards, 90-day validity encourages automation
- Open: Open source tools and transparent operations
Certificate types supported
- Single domain (example.com)
- Multiple domains / SAN certificates (up to 100 domains)
- Wildcard certificates (*.example.com)
- ECDSA and RSA key types
What's NOT supported
- Organization Validation (OV) certificates
- Extended Validation (EV) certificates
- Email certificates (S/MIME)
- Code signing certificates
- Certificates valid for longer than 90 days
š ACME Challenge Types
To prove you control a domain, Let's Encrypt requires you to complete a challenge. There are three types:
HTTP-01 Challenge
How it works: Let's Encrypt requests a file at http://your-domain/.well-known/acme-challenge/TOKEN
ā Pros:
- Easy to automate with web servers
- Works with standard HTTP on port 80
- No DNS configuration needed
- Most common and recommended for single domains
ā Cons:
- Requires port 80 to be accessible from internet
- Cannot be used for wildcard certificates
- Doesn't work if port 80 is blocked by firewall
Use when: You have a public web server and want single or multi-domain certificates.
DNS-01 Challenge
How it works: Create a TXT record at _acme-challenge.your-domain with a specific value
ā Pros:
- Works for wildcard certificates (*.example.com)
- No need for public web server
- Works behind firewalls
- Can validate internal/private domains
ā Cons:
- Requires DNS provider API access for automation
- More complex to set up initially
- DNS propagation can cause delays (up to 2 hours)
- API credentials need secure storage
Use when: You need wildcard certificates or server is not publicly accessible.
TLS-ALPN-01 Challenge
How it works: Validation via TLS connection on port 443 using ALPN extension
ā Pros:
- Works when port 80 is blocked but 443 is open
- No DNS configuration needed
- Faster than DNS-01 (no propagation delay)
ā Cons:
- Cannot be used for wildcard certificates
- Less client support (not all ACME clients support it)
- More complex to implement
Use when: Port 80 is blocked but you need automated certificates without DNS.
š Wildcard Certificates
What are wildcard certificates?
A wildcard certificate covers all subdomains at one level. For example, *.example.com covers:
- www.example.com
- api.example.com
- blog.example.com
But NOT: example.com (apex domain) or subdomain.api.example.com (nested subdomain)
How to get wildcard certificates
# Using certbot with DNS challenge (example: Cloudflare)
certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials ~/.secrets/cloudflare.ini \
-d example.com \
-d "*.example.com"
# Using acme.sh with DNS challenge
acme.sh --issue \
--dns dns_cloudflare \
-d example.com \
-d "*.example.com"ā ļø Note: Wildcard certificates REQUIRE DNS-01 challenge. HTTP-01 and TLS-ALPN-01 won't work.
Best practices for wildcards
- Always include both apex domain and wildcard:
-d example.com -d "*.example.com" - Use DNS provider with good API support for automation
- Store DNS API credentials securely (not in code)
- Consider using specific subdomains instead if you only need a few
- Monitor certificate expiry (90 days validity)
Supported DNS providers
Popular providers with ACME client support:
- Cloudflare (certbot-dns-cloudflare)
- Route 53 (certbot-dns-route53)
- Google Cloud DNS (certbot-dns-google)
- DigitalOcean (certbot-dns-digitalocean)
- Namecheap, GoDaddy, and 100+ others via acme.sh
ā ļø Rate Limits
Let's Encrypt has rate limits to prevent abuse and ensure fair usage. These limits reset on a rolling basis.
Main rate limits
- Certificates per Registered Domain: 50 per week
Example: 50 certificates for *.example.com, www.example.com, etc. per week - Duplicate Certificate: 5 per week
Same exact set of domains counts as duplicate - Failed Validations: 5 per account, per hostname, per hour
Too many failed challenges will temporarily block issuance - Accounts per IP: 10 per 3 hours
Limit on creating new ACME accounts - Pending Authorizations: 300 per account
Challenges waiting for validation
How to avoid hitting limits
- Use staging environment for testing (
--stagingflag) - Don't recreate identical certificates (cache them instead)
- Use multi-domain certificates instead of many single-domain certs
- Fix validation issues before retrying (check logs)
- Monitor expiry and renew 30 days before expiration
- Coordinate with team to avoid duplicate requests
Testing with staging environment
# Certbot staging
certbot certonly --staging -d example.com
# acme.sh staging
acme.sh --issue --staging -d example.com -w /var/www/html
# Once working, use production (remove --staging)Staging environment has much higher rate limits (same limits Ć 1000) but issues certificates from a test CA (not trusted by browsers).
Check your rate limit status
Visit crt.sh and search for your domain to see all issued certificates.
https://crt.sh/?q=example.comš Certificate Renewal & Automation
Why 90-day validity?
Let's Encrypt certificates are valid for 90 days (not configurable). This is intentional to:
- Encourage automation (manual renewal 4Ć per year is tedious)
- Limit damage from compromised private keys
- Reduce impact of mis-issued certificates
- Enable agile security improvements
Renewal timing
Recommended renewal schedule:
- 30 days before expiry: Standard renewal window (certbot default)
- 60 days: Certificate still has 30 days validity remaining
- 15-7 days: Fallback/backup renewal attempts
- Never: Wait until last day (no room for errors)
Automatic renewal with certbot
# Test renewal (dry run)
certbot renew --dry-run
# Renew all certificates (only renews if <30 days to expiry)
certbot renew
# Force renewal of specific certificate
certbot renew --cert-name example.com --force-renewalCertbot installs a systemd timer (or cron job) that runs twice daily to check for renewals.
Check certbot renewal timer (systemd)
# Check if timer is active
systemctl status certbot.timer
# View timer schedule
systemctl list-timers certbot.timer
# Manually trigger renewal check
systemctl start certbot.serviceAutomatic renewal with acme.sh
# acme.sh installs a cron job automatically
# Check cron installation
crontab -l | grep acme.sh
# Manually trigger renewal check (only renews if <60 days to expiry)
acme.sh --renew-all
# Force renewal
acme.sh --renew -d example.com --forcePost-renewal hooks
After renewal, you often need to reload/restart services to use the new certificate:
# Certbot: Add deploy hook
certbot renew --deploy-hook "systemctl reload nginx"
# Or in renewal config: /etc/letsencrypt/renewal/example.com.conf
renew_hook = systemctl reload nginx
# acme.sh: Add reloadcmd
acme.sh --install-cert -d example.com \
--cert-file /etc/nginx/certs/cert.pem \
--key-file /etc/nginx/certs/key.pem \
--fullchain-file /etc/nginx/certs/fullchain.pem \
--reloadcmd "systemctl reload nginx"Monitoring certificate expiry
- Set up monitoring alerts (30, 7, 1 days before expiry)
- Use external monitoring: SSL Labs, Uptime Robot, StatusCake
- Check renewal logs regularly
- Test renewal process quarterly
š§ Common Issues & Solutions
"too many certificates already issued for: example.com"
Problem: Hit the 50 certificates per week limit
Solutions:
- Wait 7 days for rate limit reset (it's a rolling window)
- Reuse existing certificates instead of creating new ones
- Combine multiple domains into one certificate (SAN)
- Check crt.sh to see what certificates were issued
"Connection refused" or "Timeout" during HTTP-01 challenge
Problem: Let's Encrypt can't reach your web server on port 80
Solutions:
- Verify port 80 is open:
nc -zv your-domain 80 - Check firewall rules (ufw, iptables, cloud provider security groups)
- Verify DNS points to correct IP:
dig your-domain - Ensure web server is running and serving .well-known directory
- Consider using DNS-01 challenge if port 80 can't be opened
DNS-01 challenge not working
Problem: TXT record not found or incorrect
Solutions:
- Verify TXT record:
dig _acme-challenge.example.com TXT - Wait for DNS propagation (can take 2-120 minutes)
- Check DNS provider API credentials are correct
- Some DNS providers need special permissions for TXT records
- Test with staging environment first
Certificate not trusted in browser
Problem: Browser shows "Not Secure" warning
Solutions:
- Verify certificate is from production (not staging)
- Check certificate dates (not expired, not future-dated)
- Ensure certificate includes correct domain names
- Install full certificate chain (not just leaf certificate)
- Check for mixed content (loading HTTP resources on HTTPS page)
"Renewal failed" errors
Problem: Automatic renewal is failing
Solutions:
- Check logs:
/var/log/letsencrypt/letsencrypt.log - Test renewal manually:
certbot renew --dry-run - Verify cron/systemd timer is running
- Check for configuration changes since last successful renewal
- Ensure server has internet access to Let's Encrypt API
ā Best Practices
Certificate management
- Always test with
--stagingor--dry-runfirst - Set up automatic renewal (don't rely on manual processes)
- Use post-renewal hooks to reload services automatically
- Keep ACME client (certbot/acme.sh) updated
- Back up certificates and account keys
Security
- Protect account private key (
/etc/letsencrypt/accounts/) - Use ECDSA certificates for better performance (optional)
- Enable OCSP stapling in web server config
- Set proper file permissions (600 for keys, 644 for certs)
- Use HSTS header after confirming HTTPS works
Monitoring
- Set up expiry alerts (30, 7, 1 days before expiration)
- Monitor renewal logs for failures
- Use external monitoring services (SSL Labs, StatusCake)
- Document your renewal process for team
- Test disaster recovery (certificate re-issuance)
Production deployment
- Use DNS-01 for wildcard certificates
- Use HTTP-01 for simple single/multi-domain certificates
- Combine multiple subdomains into one certificate when possible
- Keep renewal buffer time (renew 30 days before expiry)
- Have backup plan if Let's Encrypt is down (cached certificates valid for days/weeks)
š Certificate Chain Structure
Let's Encrypt certificate hierarchy
Let's Encrypt uses a two-tier hierarchy with ISRG (Internet Security Research Group) root certificates:
ISRG Root X1 (RSA 4096, expires 2035)
āāā R3 (RSA 2048, intermediate CA)
āāā Your Certificate (leaf)
ISRG Root X2 (ECDSA P-384, expires 2035)
āāā R10 (ECDSA P-384, intermediate CA)
ā āāā Your Certificate (leaf, ECDSA)
āāā R11 (ECDSA P-384, intermediate CA)
āāā Your Certificate (leaf, ECDSA)- R3: Current RSA intermediate (most common)
- R10/R11: ECDSA intermediates for ECDSA certificates
- ISRG Root X1: Trusted by all major browsers since 2016
- ISRG Root X2: ECDSA root for future-proofing
Cross-signing and compatibility
Let's Encrypt certificates are trusted by 99%+ of browsers, including:
- Chrome 50+ (April 2016)
- Firefox 50+ (November 2016)
- Safari 10+ (macOS 10.12+, iOS 10+)
- Edge (all versions)
- Android 7.1.1+ (January 2017)
- Windows 10+ (built-in trust)
ā ļø Legacy compatibility: Android 7.0 and older require special configuration. As of September 2021, Let's Encrypt stopped cross-signing with DST Root CA X3, which may affect very old devices.
Verify certificate chain
# Check certificate chain from your server
openssl s_client -connect example.com:443 -servername example.com
# Verify chain locally
openssl verify -CAfile chain.pem cert.pem
# View full chain details
openssl s_client -connect example.com:443 -showcertsš Alternative ACME Certificate Authorities
While Let's Encrypt is the most popular, other CAs also support the ACME protocol:
ZeroSSL
- Free certificates with 90-day validity
- ACME support via acme.sh and certbot
- Web dashboard for certificate management
- Commercial plans available for extended validation
# Use with acme.sh
acme.sh --set-default-ca --server zerossl
acme.sh --issue -d example.com -w /var/www/htmlBuypass Go SSL
- Free certificates with 180-day validity (longer than Let's Encrypt)
- Based in Norway, follows EU data regulations
- ACME support available
- Good alternative for European deployments
# Use with certbot
certbot certonly \
--server https://api.buypass.com/acme/directory \
-d example.com
# Use with acme.sh
acme.sh --set-default-ca --server buypass
acme.sh --issue -d example.com -w /var/www/htmlGoogle Trust Services
- Public ACME CA launched in 2022
- 90-day certificate validity
- Integration with Google Cloud Platform
- Enterprise-grade infrastructure
# Use with certbot
certbot certonly \
--server https://dv.acme-v02.api.pki.goog/directory \
-d example.comChoosing an ACME CA
| CA | Validity | Best For |
|---|---|---|
| Let's Encrypt | 90 days | Most deployments, best ecosystem |
| ZeroSSL | 90 days | Dashboard UI, commercial options |
| Buypass | 180 days | Longer validity, EU compliance |
| Google Trust | 90 days | GCP integration, enterprise needs |
š ļø Popular ACME Clients
Certbot (official)
- Official EFF client, most popular
- Written in Python
- Excellent documentation
- Many plugins for DNS providers and web servers
- Best for: Beginners, standard deployments
acme.sh
- Pure shell script (bash), no dependencies
- Supports 100+ DNS providers
- Lightweight and fast
- Best for: Automated environments, minimal systems
Caddy web server
- Built-in automatic HTTPS with Let's Encrypt
- Zero configuration needed
- Handles issuance and renewal automatically
- Best for: New deployments, simple setups
Traefik reverse proxy
- Built-in Let's Encrypt support
- Automatic certificate management
- Great for container/microservices
- Best for: Docker, Kubernetes environments
š Additional Resources
- Official Documentation: letsencrypt.org/docs
- Rate Limits: letsencrypt.org/docs/rate-limits
- Certificate Transparency: crt.sh (check issued certificates)
- ACME Specification: RFC 8555
- Community Forum: community.letsencrypt.org