Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

The ACME Protocol Explained

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.

ACME Protocol OverviewACME ClientCertbot, CaddyACME CALet's EncryptHTTPS + JWS1. Directory2. Account3. Order4. Challenge5. Issue Cert6. Download7. RenewRFC 8555 state machine — repeat before cert expiry
The ACME Protocol Explained: directory lookup through renewal forms a repeatable automation loop

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

  1. Install Certbot and your web server plugin:
sudo apt update
sudo apt install certbot python3-certbot-nginx
# or: python3-certbot-apache
  1. Ensure DNS A/AAAA records point to the server. Port 80 must reach the host for HTTP-01.
  2. 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/.

  1. 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_certificate directive — not just the leaf cert.
  • ssl_certificate_key points 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.

ACME Challenge TypesHTTP-01Port 80 file/.well-known/acmeSingle hostnameDNS-01TXT record_acme-challengeWildcards OKTLS-ALPN-01Port 443 ALPNSpecial certLess commonCA validates proof, then authorizes certificate orderPick HTTP-01 for simple vhosts; DNS-01 for wildcards and internal servers
ACME challenge types compared — HTTP-01, DNS-01, and TLS-ALPN-01 proof mechanisms

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.

ChallengePortWildcardBest forCommon failure
HTTP-0180NoSingle-site Nginx/Apache vhostsRedirect loops, CDN caching
DNS-01NoneYesWildcards, internal servers, load balancersTXT propagation delay
TLS-ALPN-01443NoCaddy, specialised proxiesALPN 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.

ACME Renewal TimelineDay 0 — IssuedDay 60 — Renew windowDay 90certbot renewsystemd timer / cronNew ACME orderRepeat challengeReload web servernginx -s reloadMonitor expiry — do not rely on memoryAlert at 21 days if renew has not succeeded
ACME renewal window — automate at day 60, alert well before day 90 expiry

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.

CriteriaACME (Let's Encrypt)Manual CA workflow
CostFree (Rs 0)Rs 3,000–15,000+/year per cert (~USD 22–110+)
RenewalAutomated via cron/timerManual CSR and upload
WildcardDNS-01 automation requiredOften manual DNS approval
Validation timeSeconds to minutesHours to days
Best fitPublic websites, APIs, microservicesEV 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.

Common ACME FailuresPort 80 blockedUFW / cloud SG / ISPCDN proxyOrange-cloud HTTP-01 failStale cron pathSymlink deploy driftDNS lagTXT not propagatedFix: certbot renew --dry-run after every deploy
The ACME Protocol Explained: four production failures that pass manual checks but break renewal

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-run after 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

ACME (Automatic Certificate Management Environment) is RFC 8555 — a JSON-over-HTTPS standard where servers prove domain control and automate X.509 certificate issuance, renewal, and revocation.

ACME via Let's Encrypt is free (Rs 0). Commercial manual SSL often costs Rs 3,000–15,000+ per year (~USD 22–110+).

Let's Encrypt certificates expire after 90 days. ACME clients typically renew around 30 days before expiry.

Your ACME client holds a key pair and contacts the CA directory URL. It registers an account, opens an order for one or more domain names, completes per-domain authorization challenges, downloads the issued X.509 certificate, and repeats the cycle before expiry. Requests and responses are signed with JSON Web Signature. Clients like Certbot, Caddy, and acme.sh handle this state machine automatically — you rarely hand-craft the JSON yourself.

On Ubuntu 22.04 or 24.04 with Nginx or Apache, install Certbot and the matching plugin via apt, confirm DNS A/AAAA records point to the server, and ensure port 80 is reachable for HTTP-01. Run certbot with your domain flags in interactive mode first. Certbot contacts the Let's Encrypt directory, completes challenges, writes SSL blocks, and stores certificates under /etc/letsencrypt/live/. Finish with certbot renew --dry-run and confirm the systemd timer is active.

HTTP-01 serves a token at http://yourdomain/.well-known/acme-challenge/ on port 80 — the default for single-hostname Nginx or Apache vhosts. DNS-01 adds a TXT record at _acme-challenge.yourdomain and is required for wildcard certificates. TLS-ALPN-01 validates over port 443 with a temporary self-signed certificate — Caddy uses this internally. Picking the wrong type causes silent automation failures until expiry nears.

Use DNS-01 when you need wildcard certificates, when port 80 cannot reach your origin, or for internal servers and load balancers behind split-horizon DNS. The trade-off is API access to your DNS provider — Certbot offers plugins for Cloudflare, Route53, and others. On domain setups in Nepal, confirm your registrar exposes an API before promising wildcard automation. HTTP-01 cannot issue wildcards and fails if firewalls, CDN caching, or redirect loops block the challenge path.

ACME clients renew roughly 30 days before the 90-day expiry by reusing your account key and repeating authorization — though CAs may cache valid authorizations briefly. Certbot supports --deploy-hook and --renew-hook scripts to reload Nginx or Apache after successful renewal without restarting during peak traffic. Parse /var/log/letsencrypt/letsencrypt.log after each cron cycle. Pair automation with expiry monitoring at 21 days — certificate alerts belong in the same tier as disk-space warnings.

The existing certificate keeps working until its notAfter date. After expiry, browsers show certificate errors and visitors may abandon checkout or form submissions on affected sites. Failed dry-runs should be treated as production incidents. Common causes include firewall changes, CDN misconfiguration, stale cron paths after Deployer symlink swaps, and Let's Encrypt rate limits from repeated failed validation during bad deploy loops. Monitoring and log review prevent surprise outages.

No. RFC 8555 is an open IETF standard published in 2019. Let's Encrypt popularised ACME, but other certificate authorities including ZeroSSL, Buypass, and some enterprise vendors support compatible directory endpoints. Clients like Certbot and Caddy let you point at different directory URLs. The production Let's Encrypt directory is at https://acme-v02.api.letsencrypt.org/directory, but the protocol itself is CA-agnostic.

Public CAs require publicly resolvable domain proof through one of the standard challenge types. Pure internal-only hostnames or .local names are not eligible from Let's Encrypt. Internal domains need a private CA or DNS-01 validation against a public suffix you control. For air-gapped networks or custom trust stores, manual enterprise CA workflows or internal PKI remain the practical choice rather than public ACME endpoints.

Let's Encrypt operates a staging ACME directory with higher rate limits and untrusted certificates for testing automation before production. Point Certbot there with the --staging flag or set server = https://acme-staging-v02.api.letsencrypt.org/directory in /etc/letsencrypt/cli.ini during development. Remove staging configuration before go-live — browsers reject staging certificates even when issuance succeeds. Always test bulk issuance scripts against staging first because production rate limits recover slowly.

ACME wins for almost every public web project — Laravel apps, WordPress sites, APIs, and microservices. It offers free certificates, automated renewal via cron or systemd timers, and validation in seconds to minutes. Manual CA workflows still appear for EV certificates with extended validation paperwork, internal PKI, air-gapped networks, or offline HSM requirements. Do not buy a commercial cert solely because free feels risky — Let's Encrypt secures a huge share of the public web.

Laravel apps rarely run ACME inside PHP — TLS termination happens at Nginx, Apache, Caddy, or a cloud load balancer. Ensure the edge server completes challenges, serves the full certificate chain in ssl_certificate directives, and redirects HTTP to HTTPS on port 80. Force HTTPS in Laravel middleware or at the proxy with URL::forceScheme('https') when behind TLS termination. Wildcard certs for multi-tenant subdomains need DNS-01 planned from day one. Mixed-content breaks forms and session cookies even when the certificate itself is valid.

HTTP-01 fails from redirect loops, CDN caching, or Nginx location blocks blocking /.well-known/acme-challenge/. On Deployer-style hosts, cron pointing at an old release path leaves the webroot challenge directory unreachable while certificates live in /etc/letsencrypt — keep /var/www/acme-challenge outside symlinked releases. Certbot logs each ACME request URL; inspect JSON status fields like pending, invalid, or valid in a formatter. After server changes, firewall edits, or deploy path updates, always run certbot renew --dry-run and verify renewal hooks reload — not restart — your web server.

Share this article

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.

Quick Contact Options
Choose how you want to connect me: