Certificates from the inside out: what the chain actually proves, HTTP-01 versus DNS-01, renewals that stop silently, rate limits, HSTS as a one-way door, and how to read the error messages that send everyone in the wrong direction.
TLS is the one piece of production infrastructure that fails on a schedule. A certificate is valid for a fixed period, renewal is automated by someone who then leaves the team, and ninety days later a browser tells your customers your site is dangerous. It is entirely preventable, and it happens constantly.
This guide covers what a certificate proves, how automated issuance actually works, the failure modes that produce misleading errors, and the configuration decisions — particularly one of them — that are much harder to undo than to make.
A public certificate makes one narrow claim: at issuance time, the requester demonstrated control over this domain name. It says nothing about the organisation being trustworthy, the application being secure, or the data being handled well.
That narrowness is worth internalising, because it explains everything about how issuance works. The certificate authority is not verifying who you are. It is verifying that you can do something only the domain's controller could do — put a file at a specific URL, or create a specific DNS record. Every issuance failure is ultimately a failure of that proof.
Three certificates matter. The leaf is yours. The root lives in the client's trust store. The intermediate connects them, and it is the one that causes trouble, because your server must send it — clients are not required to fetch it themselves.
echo | openssl s_client -servername example.com -connect example.com:443 -showcerts 2>/dev/null \
| grep -c "BEGIN CERTIFICATE"
If that returns 1, you are serving only the leaf. Browsers will often still work, because they cache intermediates from previous sites, while command-line tools, mobile apps and payment providers fail outright. The classic symptom is "it works in my browser but the API integration says the certificate is untrusted", and the fix is to serve the full chain file rather than the bare certificate.
Automated certificate issuance follows a fixed dance. Your client asks for a certificate, the authority responds with a challenge, your client makes the challenge answerable, the authority verifies it and issues. Because it is a protocol rather than a web form, it can run unattended — which is the entire point, since certificates are short-lived by design.
Short lifetimes are a feature. A compromised key is dangerous for weeks rather than years, and a renewal process that runs every sixty days is a process you find out about when it breaks, not one that quietly rots for two years and fails at 3am on a public holiday.
The HTTP challenge asks you to serve a specific file at a well-known path. The authority resolves your domain, connects on port 80, and fetches it.
Every requirement in that sentence is a way to fail. The domain must resolve to the server running the client. Port 80 must be open — a firewall that allows only 443 breaks issuance, which surprises people who assume TLS has nothing to do with plain HTTP. Redirects to HTTPS are followed, but a redirect to a different host is not. And any web server rule that blocks unknown paths will happily block the challenge too.
Here is a specific and very common scenario. You are moving a site to a new server. You update the A record. Issuance on the new server fails with a 404 on the challenge path — but you can fetch that exact URL yourself and it works.
The cause is almost always an AAAA record still pointing at the old server. Certificate authorities prefer IPv6 where it exists, so validation goes to the old machine, which knows nothing about the challenge. The error mentions a 404 and never mentions IPv6, so the natural response is to debug the web server configuration — which is fine, and not the problem.
dig +short A example.com @1.1.1.1
dig +short AAAA example.com @1.1.1.1
Change both records together, or remove AAAA until the migration is complete.
The DNS challenge asks you to publish a specific TXT record. It is more setup — your client needs API credentials for your DNS provider — but it buys two things nothing else offers.
Wildcard certificates require it. And because the proof lives in DNS rather than on the server, you can obtain a certificate for a machine that the domain does not point at yet. During a migration that removes the awkward window where the new server is live but not yet trusted.
Scope the API credentials as tightly as your provider allows. A token that can edit every record in your zone is a token that can redirect your domain, and it is sitting on a web server.
Most clients offer three ways to answer an HTTP challenge. Integrated mode edits your web server configuration for you. Webroot writes the challenge file into a directory your existing web server already serves. Standalone starts a temporary server on port 80, which requires stopping the real one.
Webroot is usually the right choice for a running site: no downtime, no automated edits to your configuration. Standalone is useful for a first issuance before the web server is configured — but if you use it for the initial certificate, make sure renewals are switched to a mode that does not require stopping anything, or every renewal becomes a small outage.
Issuance is the part you watch. Renewal is the part that fails, and it fails quietly: the timer was disabled during maintenance, the challenge method stopped working after a configuration change, the API token expired.
sudo certbot renew --dry-run
systemctl list-timers | grep -i certbot
The dry run performs a real renewal against the staging environment. Run it after every change to your web server configuration, firewall or DNS — those are exactly the changes that break renewal without touching the currently valid certificate, so the damage is invisible for weeks.
Do not rely on the renewal process reporting its own health. Check what the world sees.
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null \
| openssl x509 -noout -enddate
Alert when fewer than twenty days remain. With a sixty-day renewal cycle that gives you a comfortable window to fix a broken renewal calmly, and it catches the case where the certificate on disk renewed correctly but the web server was never reloaded — a genuinely common failure, because everything looks healthy except the certificate actually being served.
Public certificate authorities limit how many certificates you may request per domain per week. It is generous for normal use and easy to exhaust while debugging, precisely when you are retrying in a loop.
Use the staging environment while troubleshooting. Staging certificates are not trusted by browsers, which is fine — you are testing whether issuance succeeds, not whether the padlock appears. Switch to production once the process works.
Every publicly trusted certificate is logged to append-only public logs. Anyone can search them. This is excellent for detecting misissuance and inconvenient if you assumed an unlinked hostname was private.
Issue a certificate for a staging or internal hostname and you have published its existence to the world, where automated scanners will find it within hours. If a hostname should not be public knowledge, use a wildcard certificate — which does not reveal individual names — or an internal certificate authority for internal services.
Strict transport security tells browsers to refuse plain HTTP for your domain for a stated period. It closes a real attack window on first connection over a hostile network.
# Django
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = False
Two warnings. INCLUDE_SUBDOMAINS applies to every subdomain, including that internal
tool still running on plain HTTP — it will simply stop being reachable. And preloading ships your
domain inside browsers themselves; removal takes months to propagate. Start with a short duration,
confirm every subdomain works over HTTPS, then raise it. Enable preload only when you are certain,
because you cannot quickly undo it.
Default configurations tend to lag. Aim for TLS 1.2 and 1.3 only, with modern cipher suites, and leave the older protocols disabled.
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_stapling on;
ssl_stapling_verify on;
Stapling lets your server present a signed freshness proof for its own certificate, sparing clients a separate lookup. It is a small latency win and a meaningful privacy improvement, since clients no longer announce to a third party which site they are visiting.
A request for the plain-HTTP, non-canonical hostname should reach its destination in a single redirect. Chains of three or four are common — HTTP to HTTPS, then apex to www, then trailing slash — and each one costs a round trip on a mobile connection.
curl -sIL http://example.com/ | grep -E "^HTTP|^location"
Ordinary TLS authenticates the server to the client. Mutual TLS authenticates both directions, with the client presenting its own certificate. It appears in machine-to-machine integrations, government interfaces and internal service meshes.
Two practical notes. The private key must live somewhere your application can read and nothing else can — never inside a directory your web server can serve. And the client must trust the server's chain, which for private authorities means installing that chain explicitly rather than relying on the system trust store.
Three messages account for most confusion. "Unable to verify the first certificate" means the chain is incomplete — serve the intermediates. "Self-signed certificate in certificate chain" usually means a private root that the client does not trust, so supply the CA bundle explicitly. And a 404 during validation, as covered above, is far more often a DNS record pointing somewhere unexpected than an actual web server problem.
The habit worth building: before debugging the server, confirm which machine the name resolves to, over both address families.
A certificate proves domain control at a moment in time, nothing more. Serve the full chain, or non-browser clients will fail while your browser looks fine. Understand both challenge types and pick DNS-01 when you need wildcards or want to issue before a cutover. Treat renewal as the part that breaks: dry-run it after every infrastructure change, and monitor expiry from outside so you also catch the certificate that renewed but was never reloaded. Use staging while debugging to avoid rate limits. Remember that issuance publishes your hostnames. And approach HSTS with respect — subdomain inclusion and preloading are much easier to switch on than off.