ACME challenge failed, connection refused, or rate limited, a systematic checklist for when automatic HTTPS doesn’t come up.
How issuance fails, structurally
Automatic HTTPS has three prerequisites: the domain resolves to your server, the ACME challenge can reach it (port 80 for HTTP-01), and you are not rate limited from earlier failed attempts. Every "certificate not issued" case is one of those three, and the fastest path is to check them in that order rather than re-deploying and hoping. Crucially: your proxy logs contain the exact ACME error naming the failing check, read them first.
docker logs 2>&1 | grep -i -E "acme|certificate|challenge" | tail -20
Check 1: DNS (it’s DNS 80% of the time)
The domain must resolve to this server’s public IP before issuance can succeed:
No answer: the record does not exist, or you edited a zone that is not authoritative (registrar DNS vs Cloudflare is the classic mix-up)
Wrong IP: an old record, or the record points at a load balancer/other box
Also check AAAA: if an IPv6 record exists but the server does not actually serve on that address, validation can fail even though IPv4 looks perfect
dig +short app.example.com # what the world sees
curl -4 -s ifconfig.me # this server's public IPv4
# these two must match
Check 2: reachability on port 80
HTTP-01 validation arrives as a plain HTTP request on port 80. Both the cloud firewall (security group) and any host firewall (ufw, iptables) must allow 80 and 443, and the proxy container must actually be running and bound to them. A stray host-level nginx or Apache holding port 80 silently absorbs every challenge, check with ss -tlnp | grep -E ":80|:443" that the listener is your proxy.
Check 3: the Cloudflare orange cloud
If the domain is proxied through Cloudflare (orange cloud), challenge requests hit Cloudflare’s edge, not your origin, and HTTP-01 can fail confusingly. Two clean resolutions: set the Cloudflare SSL mode to Full (strict) and let Cloudflare terminate for visitors while your origin still gets its own certificate; or temporarily grey-cloud the DNS record, let issuance complete, then re-enable the proxy. Never run Flexible mode; it causes redirect loops with origin HTTPS.
Check 4: rate limits, and how not to hit them
Let’s Encrypt limits failed validations to 5 per account, per hostname, per hour, and duplicate certificates to 5 per week. Retrying in a loop while DNS is broken burns through both. The discipline: diagnose with the checks above, fix the root cause, retry once. For experiments, point the proxy at the staging endpoint, generous limits and untrusted certificates, perfect for verifying plumbing before touching production limits.
When everything is fixed, issuance is fast: certificates typically arrive within seconds of the first valid request, and platforms like Peon retry automatically, so a previously failing domain heals on its own once DNS and firewall are right.
Top comments (0)