
September 12, 2026
12 min read
By Kokil Thapa | Last reviewed: September 2026
The ACME Protocol Explained starts with a practical problem. Every production site needs valid TLS certificates. Manual issuance and renewal breaks at scale. You forget a deadline. A staging cert lands on production. A wildcard needs DNS API access you never wired up. The ACME protocol solves this by standardising how a certificate authority and your server negotiate issuance over HTTPS. If you run Linux servers with Let's Encrypt and Certbot, you already depend on ACME daily — even when you never read RFC 8555.
What is the ACME protocol and how does it work?
ACME stands for Automatic Certificate Management Environment. The IETF published it as RFC 8555 in 2019. It replaced the earlier draft used by Let's Encrypt at launch. Today, Caddy, Traefik, Certbot, acme.sh, and many load balancers speak ACME natively.
Think of ACME as a state machine between two parties. Your ACME client holds a key pair. The certificate authority exposes a directory URL with endpoint templates. The client registers an account, proves it controls each domain, submits a certificate order, downloads the issued cert, and repeats the cycle before expiry.
Core ACME objects you will encounter
Every ACME conversation uses a small set of JSON resources. You rarely hand-craft them. Still, knowing the names helps when Certbot logs fail mid-order.
- Directory — CA metadata listing endpoint URLs. Let's Encrypt production directory is at
https://acme-v02.api.letsencrypt.org/directory. - Account — ties your client key to contact info and rate-limit buckets.
- Order — one certificate request covering one or more identifiers (domain names).
- Authorization — per-domain proof requirement before issuance.
- Challenge — the exact proof method (HTTP-01, DNS-01, TLS-ALPN-01).
- Certificate — the issued X.509 chain URL once validation succeeds.
Requests and responses are signed with JSON Web Signature (JWS). The outer payload is often base64url-encoded. When debugging, a base64 encoder and decoder helps inspect JWK thumbprints offline.
Why ACME replaced manual CSR workflows
Before ACME, you generated a CSR, pasted it into a CA portal, completed email validation, downloaded files, and installed them by hand. Renewal meant repeating the entire process. ACME inverts that. The client drives the workflow programmatically. Cron or systemd timers handle renewal. On sister sites I maintain with Deployer 7 and GitLab CI, stale certificates are now the exception — not the quarterly fire drill they once were.
That reliability matters for SEO and trust. Browsers flag expired TLS. Search engines treat HTTPS as baseline. A law-firm portal or legal information site with valid HTTPS avoids the credibility hit of browser warnings during document uploads.
How do you set up ACME with Certbot on Ubuntu?
Certbot remains the most common ACME client on Ubuntu servers running Apache or Nginx. It wraps the protocol, places challenge files, edits vhost config, and installs renewal hooks. Below is the flow I use on production Ubuntu 22.04 and 24.04 hosts with PHP-FPM.
Install Certbot and request your first certificate
- Install Certbot and your web server plugin:
sudo apt update
sudo apt install certbot python3-certbot-nginx
# or: python3-certbot-apache - Ensure DNS A/AAAA records point to the server. Port 80 must reach the host for HTTP-01.
- Run Certbot in interactive mode first:
sudo certbot --nginx -d example.com -d www.example.com Certbot contacts the Let's Encrypt ACME directory, creates an account if needed, opens an order, completes HTTP-01 challenges, downloads the certificate, and writes Nginx SSL blocks. Certificates land in /etc/letsencrypt/live/example.com/.
- Verify auto-renewal dry-run:
sudo certbot renew --dry-run If the dry-run passes, systemd timer or cron handles real renewals. On shared EC2 hosts where I run multiple vhosts, I also confirm the timer is active:
systemctl list-timers | grep certbot Post-issuance server checklist
ACME gives you files. Your stack must use them correctly. After every first issuance, I verify four items.
- Full chain in the
ssl_certificatedirective — not just the leaf cert. ssl_certificate_keypoints to the private key with correct permissions (root-owned, not world-readable).- HTTP to HTTPS redirect exists on port 80.
- PHP-FPM or app reload happens in a renewal deploy hook if the app caches TLS metadata.
For Apache-to-Nginx migrations, align ACME paths before cutover. Our Apache to Nginx migration guide covers vhost timing so challenges do not fail mid-switch.
What are the different ACME challenge types?
ACME proves domain control before issuing a cert. The CA publishes a token. Your client responds in a way only the real domain owner can. Pick the wrong challenge type and automation fails silently until expiry nears.
HTTP-01: the default for public web servers
The CA fetches http://example.com/.well-known/acme-challenge/{token}. The response body must contain the token plus your account key thumbprint. Port 80 must be reachable from the public internet. Firewalls, CDN misconfiguration, or Nginx location blocks cause most HTTP-01 failures I see.
HTTP-01 cannot issue wildcard certificates. It works well for single-hostname Laravel apps, WordPress sites, and static vhosts on a single machine.
DNS-01: wildcards and split-horizon DNS
DNS-01 requires a TXT record at _acme-challenge.example.com. Wildcard certs (*.example.com) need DNS-01 exclusively. The trade-off is API access to your DNS provider. Certbot DNS plugins exist for Cloudflare, Route53, and others. On domain and hosting setups in Nepal, confirm your registrar exposes an API before promising wildcard automation to a client.
TLS-ALPN-01: port 443 validation
TLS-ALPN-01 validates over port 443 using a temporary self-signed certificate with a special ACME extension. Caddy uses this path internally. Few manual Certbot setups rely on it. Know it exists when debugging multi-protocol edge proxies.
| Challenge | Port | Wildcard | Best for | Common failure |
|---|---|---|---|---|
| HTTP-01 | 80 | No | Single-site Nginx/Apache vhosts | Redirect loops, CDN caching |
| DNS-01 | None | Yes | Wildcards, internal servers, load balancers | TXT propagation delay |
| TLS-ALPN-01 | 443 | No | Caddy, specialised proxies | ALPN negotiation blocked |
How does ACME certificate renewal work in production?
Let's Encrypt certificates expire after 90 days. ACME clients renew at roughly 30 days before expiry by default. Renewal reuses your account key and repeats authorization — though CAs may allow cached authz for a short window.
Renewal hooks and zero-downtime reloads
Certbot supports --deploy-hook and --renew-hook scripts. I use deploy hooks to reload Nginx or Apache after a successful renewal. Never restart blindly during peak traffic if a reload suffices.
sudo certbot renew --deploy-hook "systemctl reload nginx" For multi-site hosts, one failed vhost should not block others. Parse /var/log/letsencrypt/letsencrypt.log after each cron cycle. Pair this with monitoring. Certificate expiry alerts belong in the same tier as disk-space warnings.
Rate limits and failed orders
Let's Encrypt enforces rate limits per registered domain and per account. Duplicate certificate limits, failed validation limits, and orders per week can block you during a bad deploy loop. Read the official Let's Encrypt rate limits documentation before running Certbot in a CI loop against production domains.
This overlaps with API abuse patterns. Our guide on API rate limiting and abuse prevention uses different context, but the discipline is the same — backoff, idempotency, and never hammer a shared endpoint.
Staging environment: test before production
Let's Encrypt operates a staging ACME directory with higher limits and untrusted certificates. Point Certbot there while developing automation:
sudo certbot --staging --nginx -d test.example.com Or set server = https://acme-staging-v02.api.letsencrypt.org/directory in /etc/letsencrypt/cli.ini during testing. Remove staging before go-live. Browsers will reject staging certs even when issuance succeeds.
ACME vs manual SSL — which should you use?
Manual SSL still appears on legacy panels and some enterprise CAs. ACME wins for almost every web project I ship today. Exceptions exist — internal PKI, EV certificates with extended validation paperwork, or air-gapped networks.
| Criteria | ACME (Let's Encrypt) | Manual CA workflow |
|---|---|---|
| Cost | Free (Rs 0) | Rs 3,000–15,000+/year per cert (~USD 22–110+) |
| Renewal | Automated via cron/timer | Manual CSR and upload |
| Wildcard | DNS-01 automation required | Often manual DNS approval |
| Validation time | Seconds to minutes | Hours to days |
| Best fit | Public websites, APIs, microservices | EV certs, offline HSM, custom trust stores |
For a typical Laravel 12 or WordPress 7.1 site on Ubuntu, ACME is the correct default. Budget the engineering time for DNS-01 if wildcards matter. Do not buy a commercial cert solely because "free feels risky" — Let's Encrypt secures a huge share of the public web.
ACME clients beyond Certbot
Certbot is not the only client. Caddy obtains and renews certificates automatically with zero extra config — excellent for greenfield proxies. acme.sh suits shell-centric workflows and many DNS providers. Traefik and HAProxy integrate ACME for container and load-balancer estates.
On provisioning-heavy fleets, bake ACME into your Ansible playbooks. See our Ansible playbooks for PHP server provisioning for the broader server setup pattern. ACME fits naturally as a post-vhost task.
Debugging ACME with JSON logs
ACME speaks JSON. Certbot logs the URL of each request. Copy a failing order payload into a JSON formatter to inspect status fields like pending, invalid, or valid. The official Certbot documentation maps common error strings to fixes.
I've encountered renewal failures after Deployer symlink swaps when cron still pointed at an old release path. The certificate lived in /etc/letsencrypt, but the webroot challenge directory sat inside a release folder. Keep /var/www/acme-challenge or use Nginx plugins that do not depend on release paths.
How do you integrate ACME with Laravel and modern stacks?
Laravel apps rarely run ACME inside PHP. Termination happens at Nginx, Apache, Caddy, or a cloud load balancer. Your job is to ensure the edge server completes challenges and serves the full chain.
For Laravel 13 on PHP 8.3+, force HTTPS in middleware or at the proxy. Use URL::forceScheme('https') when behind TLS termination. Mixed-content breaks forms and session cookies even when the cert itself is valid.
Wildcard certs for multi-tenant subdomains (*.app.example.com) need DNS-01 from day one. Retrofitting DNS API automation after launch costs more than planning it during web development. Same applies to eCommerce checkout domains — payment gateways expect consistent HTTPS on every step.
Technical SEO treats HTTPS as a baseline signal. Valid TLS supports crawl trust and avoids referral loss from browser interstitials. Pair certificate hygiene with broader work described in our search engine optimization service and speed optimization pages — HTTP/2 and HTTP/3 often need correct cert chains at the edge.
Key Takeaways
- ACME (RFC 8555) automates certificate issuance, renewal, and revocation through signed JSON over HTTPS — manual CSR portals are no longer the default for public sites.
- Use HTTP-01 for standard Nginx/Apache vhosts; switch to DNS-01 for wildcards and when port 80 cannot reach your origin.
- Always run
certbot renew --dry-runafter server changes, deploy path updates, or firewall edits. - Test against Let's Encrypt staging before scripting bulk issuance — production rate limits recover slowly.
- Monitor expiry at 21 days even when cron exists; renewal hooks should reload — not restart — your web server.
- Keep challenge paths outside symlinked release directories on Deployer-style hosts.
People Also Ask
Is the ACME protocol only for Let's Encrypt?
No. RFC 8555 is an open standard. Let's Encrypt popularised ACME, but other CAs including ZeroSSL, Buypass, and some enterprise vendors support compatible endpoints. Clients like Certbot and Caddy let you point at different directory URLs.
What happens if ACME renewal fails?
The existing certificate keeps working until its notAfter date. After expiry, browsers show certificate errors. Visitors may abandon checkout or form submissions. Monitoring and logs prevent surprise outages — treat failed dry-runs as production incidents.
Can ACME issue certificates for internal domains?
Public CAs require publicly resolvable domain proof. Internal-only hostnames need a private CA or DNS-01 with a public suffix you control. Pure .local names are not eligible from Let's Encrypt.
Does ACME support certificate revocation?
Yes. ACME includes a revocation endpoint. Clients call it with the cert serial and account key when a private key is compromised. Certbot exposes this via certbot revoke. Revocation propagates to OCSP responders within minutes for major CAs.
Put ACME to work on your next deploy
The ACME Protocol Explained is not academic trivia. It is the mechanism keeping HTTPS cheap, current, and repeatable on the production stacks most teams run in 2026. Wire up Certbot or a native ACME client, pick the right challenge type, test against staging, and monitor renewals the same way you monitor disk space. On projects from legal portals to Notary Kathmandu and Laravel eCommerce builds, that discipline prevents the embarrassing outage that hits at the worst moment.
Need hands-on help with Let's Encrypt, Nginx TLS config, or renewal monitoring on Ubuntu? Contact us for server setup and ongoing support and maintenance. For broader automation context, read about the Model Context Protocol — another JSON protocol gaining traction in 2026, though for AI tooling rather than certificates.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

