ACME Protocol: Complete Reference Guide

Introduction to ACME

The Automated Certificate Management Environment (ACME) protocol is an IETF standard (RFC 8555) designed to automate the process of certificate issuance and domain validation. Developed by the Internet Security Research Group (ISRG) for Let’s Encrypt, ACME enables server administrators to obtain and renew SSL/TLS certificates automatically without manual intervention, significantly simplifying the deployment of HTTPS websites.

Key Benefits:

  • Complete automation of certificate issuance and renewal
  • Standardized, vendor-neutral approach to certificate management
  • Reduction in human error during certificate operations
  • Elimination of manual approval delays
  • Support for wildcard certificates
  • Free implementation via Let’s Encrypt and other Certificate Authorities (CAs)

Core Concepts

ACME Workflow Overview

StageDescription
Account CreationClient creates an account with the ACME server
Order CreationClient requests certificate for specific domains
Domain ValidationClient proves control over the requested domains
Certificate IssuanceAfter validation, server issues the certificate
Certificate RenewalProcess repeats before certificate expiration

Key Components

ComponentDescription
ACME ClientSoftware that communicates with the ACME server (e.g., Certbot, acme.sh)
ACME ServerService operated by a CA that issues certificates (e.g., Let’s Encrypt)
Account KeyRSA or ECDSA key pair used to identify and authenticate the client
Certificate KeyKey pair for which the certificate is issued
ChallengesTests that verify domain control
NonceOne-time values preventing replay attacks
JWSJSON Web Signatures used to sign requests

Challenge Types

ChallengeDescriptionUsage
HTTP-01Places a file at /.well-known/acme-challenge/{token}Standard validation for web servers
DNS-01Creates a TXT record at _acme-challenge.domain.comRequired for wildcard certificates
TLS-ALPN-01Uses TLS Application-Layer Protocol NegotiationAlternative for servers behind firewalls

ACME Protocol Flow

1. Account Registration

Client                                                Server
  |                                                     |
  | POST /newAccount                                    |
  |   JWS({"termsOfServiceAgreed": true,                |
  |        "contact": ["mailto:admin@example.com"]})    |
  |---------------------------------------------------->|
  |                                                     |
  |                HTTP 201 Created                     |
  |            {"status": "valid",                      |
  |             "orders": "https://example.com/acme/orders/123"} |
  |<----------------------------------------------------|

2. Order Creation

Client                                                Server
  |                                                     |
  | POST /newOrder                                      |
  |   JWS({"identifiers": [{"type": "dns",              |
  |                         "value": "example.com"}]})  |
  |---------------------------------------------------->|
  |                                                     |
  |                 HTTP 201 Created                    |
  |   {"status": "pending",                             |
  |    "authorizations": ["https://example.com/acme/authz/123"], |
  |    "finalize": "https://example.com/acme/finalize/123"} |
  |<----------------------------------------------------|

3. Domain Authorization

Client                                                Server
  |                                                     |
  | POST /authz/123                                     |
  |   JWS({})                                           |
  |---------------------------------------------------->|
  |                                                     |
  |                 HTTP 200 OK                         |
  |   {"status": "pending",                             |
  |    "challenges": [{"type": "http-01",               |
  |                    "url": "https://example.com/acme/chall/123", |
  |                    "token": "abc123"}]}             |
  |<----------------------------------------------------|

4. Challenge Response

Client                                                Server
  |                                                     |
  | (Client prepares challenge response)                |
  |                                                     |
  | POST /chall/123                                     |
  |   JWS({})                                           |
  |---------------------------------------------------->|
  |                                                     |
  |                 HTTP 200 OK                         |
  |   {"status": "processing"}                          |
  |<----------------------------------------------------|
  |                                                     |
  | (Server verifies the challenge)                     |
  |                                                     |
  |                 HTTP 200 OK                         |
  |   {"status": "valid"}                               |
  |<----------------------------------------------------|

5. Certificate Issuance

Client                                                Server
  |                                                     |
  | POST /finalize/123                                  |
  |   JWS({"csr": "base64url(CSR)"})                   |
  |---------------------------------------------------->|
  |                                                     |
  |                 HTTP 200 OK                         |
  |   {"status": "processing"}                          |
  |<----------------------------------------------------|
  |                                                     |
  | (Server processes the CSR)                          |
  |                                                     |
  |                 HTTP 200 OK                         |
  |   {"status": "valid",                               |
  |    "certificate": "https://example.com/acme/cert/123"} |
  |<----------------------------------------------------|
  |                                                     |
  | GET /cert/123                                       |
  |---------------------------------------------------->|
  |                                                     |
  |                 HTTP 200 OK                         |
  |   (PEM-encoded certificate chain)                   |
  |<----------------------------------------------------|

Implementation Guide

Setting Up ACME Clients

Certbot (Official Let’s Encrypt Client)

Installation:

Ubuntu/Debian:

sudo apt update
sudo apt install certbot

CentOS/RHEL:

sudo yum install epel-release
sudo yum install certbot

Basic Usage:

Issue certificate (Apache):

sudo certbot --apache -d example.com -d www.example.com

Issue certificate (Nginx):

sudo certbot --nginx -d example.com -d www.example.com

Standalone mode:

sudo certbot certonly --standalone -d example.com

DNS challenge for wildcard:

sudo certbot certonly --manual --preferred-challenges=dns -d *.example.com -d example.com

Renew all certificates:

sudo certbot renew

acme.sh (Shell Script Implementation)

Installation:

curl https://get.acme.sh | sh

Basic Usage:

Issue certificate (Webroot):

acme.sh --issue -d example.com -w /var/www/html

Issue with DNS API (Cloudflare example):

export CF_Key="cloudflare-api-key"
export CF_Email="cloudflare-email"
acme.sh --issue -d example.com -d *.example.com --dns dns_cf

Install certificate:

acme.sh --install-cert -d example.com \
  --key-file /path/to/keyfile \
  --fullchain-file /path/to/fullchain \
  --reloadcmd "service nginx reload"

Renew all certificates:

acme.sh --renew-all

Common Challenge Methods

HTTP-01 Challenge

  1. Server provides a token and key authorization
  2. Client places file at: http://<YOUR_DOMAIN>/.well-known/acme-challenge/<TOKEN>
  3. Content of file: <TOKEN>.<THUMBPRINT>
  4. ACME server validates by HTTP GET request to that URL

Example implementation:

# Certbot handles this automatically, but manual example:
mkdir -p /var/www/html/.well-known/acme-challenge/
echo "abc123.base64url(sha256(accountKey))" > /var/www/html/.well-known/acme-challenge/abc123

DNS-01 Challenge

  1. Server provides a token and key authorization
  2. Client creates TXT record: _acme-challenge.<YOUR_DOMAIN>
  3. Record value: base64url(sha256(keyAuthorization))
  4. ACME server validates by DNS lookup

Example implementation:

# Add the following TXT record
# Name: _acme-challenge.example.com
# Value: base64url(sha256(tokenValue.thumbprint))

TLS-ALPN-01 Challenge

  1. Server provides a token and key authorization
  2. Client configures TLS server to use ALPN extension “acme-tls/1”
  3. Client presents self-signed certificate with special extension
  4. ACME server validates by TLS handshake

Advanced Techniques

Wildcard Certificates

Wildcard certificates require the DNS-01 challenge:

Certbot example:

sudo certbot certonly --manual --preferred-challenges=dns \
  -d *.example.com -d example.com

acme.sh with DNS API:

acme.sh --issue -d example.com -d *.example.com --dns dns_cf

Certificate Revocation

Certbot:

sudo certbot revoke --cert-path /etc/letsencrypt/live/example.com/cert.pem

acme.sh:

acme.sh --revoke -d example.com
acme.sh --remove -d example.com  # Remove after revocation

Rate Limits (Let’s Encrypt Specific)

Limit TypeValueTime Window
Certificates per registered domain50Per week
Duplicate certificates5Per week
Failed validations5Per hour
Accounts per IP address10Per 3 hours
New orders300Per 3 hours

Certificate Deployment

Apache:

<VirtualHost *:443>
    ServerName example.com
    SSLEngine on
    SSLCertificateFile /path/to/fullchain.pem
    SSLCertificateKeyFile /path/to/privkey.pem
    # Other directives...
</VirtualHost>

Nginx:

server {
    listen 443 ssl;
    server_name example.com;
    ssl_certificate /path/to/fullchain.pem;
    ssl_certificate_key /path/to/privkey.pem;
    # Other directives...
}

Integration with Web Services

Integration with Popular Platforms

PlatformIntegration Method
Apachemod_md or Certbot with –apache flag
NginxCertbot with –nginx flag
HAProxyUse separate ACME client and configure cert paths
DockerTraefik with Let’s Encrypt integration
Kubernetescert-manager with ClusterIssuer
CaddyBuilt-in automatic HTTPS
PleskLet’s Encrypt extension
cPanelAutoSSL feature

Automating Certificate Management

Certbot with cron:

# Add to crontab:
0 0 * * * certbot renew --quiet

acme.sh with automated renewal:

# Installed by default during setup:
# 0 0 * * * "/home/user/.acme.sh"/acme.sh --cron --home "/home/user/.acme.sh" > /dev/null

Systemd timer for Certbot:

# Enable the timer:
sudo systemctl enable certbot.timer
sudo systemctl start certbot.timer

Common Challenges and Solutions

ChallengeSolution
Rate limitingImplement proper error handling and exponential backoff
DNS propagation delayAdd wait time or verification before completing DNS-01 challenges
Firewall blocking validationAllow outbound traffic from CA validation servers or use DNS validation
Certificate renewal failuresSet up monitoring and alerts for expiring certificates
Mixed content warningsUpdate all resources to use HTTPS URLs
Redirecting HTTP to HTTPSConfigure proper 301 redirects in web server
Certificate transparency loggingConfigure monitoring for unexpected certificate issuance

Best Practices

Security Recommendations

  • Keep private keys secure and use appropriate file permissions
  • Implement automated renewal at least 30 days before expiration
  • Set up monitoring for certificate expiration
  • Use the most specific certificate scope needed (avoid wildcards if unnecessary)
  • Implement HTTP Strict Transport Security (HSTS)
  • Use proper certificate chain ordering
  • Implement OCSP stapling to improve performance

Performance Optimization

  • Use appropriate key sizes (RSA 2048 or ECDSA P-256)
  • Enable HTTP/2 for improved performance with HTTPS
  • Implement OCSP stapling to reduce connection latency
  • Use session resumption and TLS ticket keys
  • Implement correct SSL/TLS caching headers
  • Consider using ECDSA certificates for faster handshakes

ACME Client Selection Guide

ClientBest ForKey Features
CertbotGeneral use, beginnersOfficial client, good documentation, plugins for web servers
acme.shAdvanced users, scriptingLightweight, rich DNS API support, low dependencies
CaddySimple web hostingBuilt-in ACME client, automatic renewal
cert-managerKubernetes environmentsNative Kubernetes integration
TraefikDocker environmentsDynamic configuration, automatic renewal
Windows ACME Simple (WACS)Windows serversWindows-native GUI and CLI
Certify The WebWindows servers with UIUser-friendly Windows interface
LegoGo developers, embeddingLibrary and CLI tool written in Go

Resources for Further Learning

Official Documentation

ACME Service Providers

ProviderURLFeatures
Let’s Encryptletsencrypt.orgFree, 90-day certificates, rate-limited
ZeroSSLzerossl.comFree tier, commercial options
BuyPass Go SSLbuypass.comFree, 180-day certificates
SSL.comssl.comCommercial, ACME support
Sectigosectigo.comCommercial, ACME support

Tools and Utilities

Community Resources

Troubleshooting Guide

Common Error Messages

ErrorPossible CausesSolutions
Connection refusedFirewall blocking ACME validationAllow inbound connections on port 80/443
Invalid response from serverWebserver misconfigurationCheck server configuration and logs
DNS problem: NXDOMAINDNS not propagated or misconfiguredWait for DNS propagation, verify records
Too many certificates issuedHit Let’s Encrypt rate limitWait for rate limit to reset, use staging environment for testing
Invalid Account ContactEmail address errorUpdate account with valid contact information
Timeout during connectNetwork issues or server unreachableCheck network connectivity, firewall rules
Unauthorized -> Invalid responseChallenge verification failureCheck challenge configuration, server accessibility

Verification Steps

  1. HTTP-01 challenge verification:

    curl -i http://example.com/.well-known/acme-challenge/TOKEN
    
  2. DNS-01 challenge verification:

    dig +short TXT _acme-challenge.example.com
    
  3. Certificate verification:

    openssl x509 -in certificate.pem -text -noout
    
  4. TLS connection test:

    openssl s_client -connect example.com:443 -servername example.com
    
  5. Certbot dry run:

    certbot renew --dry-run
    
Scroll to Top