{"course":{"slug":"cloud-and-shipping","title":"Shipping It","url":"https://addaly.com/learn/cloud-and-shipping","level":"intermediate","track":"build"},"lesson":{"slug":"tls-and-the-padlock","title":"The padlock, and what it does not prove","url":"https://addaly.com/learn/cloud-and-shipping/tls-and-the-padlock","position":6,"of":74,"minutes":9,"answer":"A certificate proves control of a domain name and nothing else, and the way it kills your site is not being broken but quietly expiring.","keyIdea":"A certificate proves control of a domain name and nothing else, and the way it kills your site is not being broken but quietly expiring."},"body":{"format":"markdown","text":"## What a certificate actually claims\n\nA TLS certificate says one thing: at the moment this was issued, whoever asked for it could prove they controlled this domain name.\n\nThat is the whole claim. It does not say the company is real, registered, honest, or the one you meant to visit. A domain-validated certificate is free and takes about thirty seconds, which means anyone who can register `add4ly-login.com` can put a padlock on it. Most phishing pages served today are on HTTPS. The padlock means the connection is private; it says nothing about who is on the other end.\n\nWhat you get from it is still worth having, and it is not optional. Without TLS, everything is readable and editable in transit: passwords, session cookies, the page itself. Public wifi and hostile ISPs have both injected ads into plain HTTP pages, at scale. Browsers now mark HTTP as not secure, HTTP/2 in practice requires TLS, and a payment provider will not talk to you without it.\n\n## Getting one for nothing\n\nLet's Encrypt issues certificates free, through an automated protocol called ACME. Practically every tool speaks it.\n\n- **Caddy** does it with no configuration at all beyond your domain name. Free.\n- **Certbot** does it for nginx and Apache, and writes the config for you. Free.\n- Any managed platform (Cloudflare, Vercel, Fly, Netlify, Render) does it for you and you never see it.\n\nPaid DV certificates from a reseller cost roughly $10 to $70 a year and give you the same padlock, plus a warranty that nobody has ever successfully claimed. Extended Validation certificates used to show the company name in the URL bar, which was the one thing you were buying; Chrome and Firefox stopped displaying it in 2019. There is no remaining reason for a small site to pay.\n\n## Ninety days, and the renewal is the outage\n\nLet's Encrypt certificates last 90 days. The convention is to renew at 60, leaving 30 days of slack for a failing cron job to be noticed.\n\nThis is where certificates actually take sites down. Not attacks: expiry. And the safety net you may be remembering is gone: Let's Encrypt stopped sending expiry-warning emails in mid-2025. Nothing will tell you. Shorter-lived certificates, measured in days rather than months, are being rolled out across the industry, which makes automatic renewal mandatory rather than merely sensible.\n\n> Figure: The life of a ninety-day certificate. Timeline: Day 0, Issued by Let's Encrypt in about thirty seconds, free; Day 60, Automatic renewal attempts, then the web server must reload; Day 69, A monitor should shout here: 21 days remaining; Day 60 to 90, Slack for a failed renewal to be found and fixed; Day 90, Expiry. Every browser shows a full-page warning. Renewal at day 60 leaves thirty days for a broken cron job to be noticed. Since mid-2025 nothing emails you, so the thing that notices has to be a monitor you set up.\n\nCheck any site's dates from a terminal:\n\n```bash\necho | openssl s_client -connect addaly.com:443 -servername addaly.com 2>/dev/null \\\n  | openssl x509 -noout -dates -subject\n```\n\nPut that in a monitor that shouts at 21 days remaining. It is five minutes of work against the single most common self-inflicted outage in this course.\n\n## The two ways of proving you own the name\n\n- **HTTP-01.** The issuer asks for a file at `/.well-known/acme-challenge/<token>` on port 80. Simple, and the default. It needs port 80 reachable from the internet, and it cannot issue wildcards.\n- **DNS-01.** You publish a TXT record at `_acme-challenge.example.com`. This works for wildcard certificates, works when the machine is not publicly reachable, and needs API access to your DNS provider.\n\nIf your renewal keeps failing, it is nearly always one of these two preconditions quietly changing: a firewall rule that closed port 80, or a DNS API token that expired.\n\n## The failures you will actually meet\n\n- **The certificate covers `example.com` but not `www.example.com`.** They are two names. Both must be on the certificate, or the visitor who types `www` gets a full-page security warning.\n- **A missing intermediate chain.** Your browser looks fine, because browsers cache intermediate certificates from other sites you have visited. `curl`, Android, and your payment provider's webhook do not. This is why \"it works in Chrome\" is not a test. Use `openssl s_client` or SSL Labs.\n- **The renewal ran but nothing reloaded.** New certificate on disk, old certificate in memory. Your renewal hook must reload the web server, and the cron user must have permission to do it.\n- **Rate limits.** Testing your automation against the production issuer will lock you out for a week. Use the staging endpoint while you are getting it working.\n- **Clock skew.** A server whose clock is wrong will reject a perfectly good certificate as not yet valid.\n\n## HSTS: the header you cannot take back\n\n`Strict-Transport-Security` tells browsers to refuse plain HTTP for your domain for a stated period.\n\n```\nStrict-Transport-Security: max-age=31536000; includeSubDomains\n```\n\nThat is a year. Every browser that has seen it will refuse to load your site over HTTP, and there is no way to reach those browsers and change your mind. If a subdomain of yours is HTTP-only, `includeSubDomains` takes it offline.\n\nStart at `max-age=300`, confirm nothing breaks, then raise it in steps. The HSTS preload list, which bakes your domain into the browsers themselves, is close to permanent: removal takes months to reach users. Do not submit a domain you are still experimenting with.\n\n## Mixed content\n\nOne `http://` image URL on an HTTPS page and the browser blocks it silently. Grep your templates and your database content for `http://` before launch, and again after anyone pastes a URL into a CMS field."},"check":{"question":"Your certificate renews automatically at 60 days and the cron job has been reporting success for months. One morning the site shows an expired-certificate warning to every visitor, and the certificate file on disk has a valid date. What is the most likely cause?","options":[{"text":"The certificate was issued for the apex domain only, so visitors typing www see a name-mismatch warning","correct":false,"tempting":"Confuses a name mismatch with an expiry warning; both are red pages, but the browser names which one it is, and a mismatch would have been failing since day one."},{"text":"Let's Encrypt revoked the certificate, which browsers checked before displaying the page","correct":false,"tempting":"Assumes browsers reliably perform live revocation checks. Most do not: revocation is the weakest part of the system, and it would not present as expiry."},{"text":"The renewal wrote a new certificate but the web server was never reloaded, so it is still serving the old one from memory","correct":true},{"text":"The clock on the server drifted, so the new certificate is being treated as not yet valid","correct":false,"tempting":"A plausible cousin, but clock skew produces a \"not yet valid\" error and would also break outgoing TLS calls from the box, which you would have noticed elsewhere."}]},"prev":{"slug":"http-and-status-codes","title":"What a status code is telling you","url":"https://addaly.com/learn/cloud-and-shipping/http-and-status-codes"},"next":{"slug":"behind-the-proxy","title":"What sits in front of your app","url":"https://addaly.com/learn/cloud-and-shipping/behind-the-proxy"},"authorship":"Founder and series editor: Dr Mustafa Mun. Written by AI models to a written standard. Exam keys checked blind by a second model: 3,665 of 3,666 agreed. Academic review invited.","review":{"status":"Draft, open for academic review"},"cite":"Addaly (2026) 'The padlock, and what it does not prove', in Shipping It. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/cloud-and-shipping/tls-and-the-padlock (Accessed: 13 October 2026).","licence":{"name":"CC BY-NC-SA 4.0","url":"https://creativecommons.org/licenses/by-nc-sa/4.0/"},"reader":{"schema":1,"title":"The padlock, and what it does not prove","courseTitle":"Shipping It","module":{"index":1,"slug":"how-the-internet-reaches-you","title":"How the internet reaches your process"},"answer":null,"keyIdea":"A certificate proves control of a domain name and nothing else, and the way it kills your site is not being broken but quietly expiring.","hero":{"kind":"figure","figure":{"t":"figure","label":"Figure","figure":{"type":"timeline","title":"The life of a ninety-day certificate","events":[{"when":"Day 0","what":"Issued by Let’s Encrypt in about thirty seconds, free"},{"when":"Day 60","what":"Automatic renewal attempts, then the web server must reload"},{"when":"Day 69","what":"A monitor should shout here: 21 days remaining"},{"when":"Day 60 to 90","what":"Slack for a failed renewal to be found and fixed"},{"when":"Day 90","what":"Expiry. Every browser shows a full-page warning"}],"caption":"Renewal at day 60 leaves thirty days for a broken cron job to be noticed. Since mid-2025 nothing emails you, so the thing that notices has to be a monitor you set up."},"alt":"Figure: The life of a ninety-day certificate. Timeline: Day 0, Issued by Let’s Encrypt in about thirty seconds, free; Day 60, Automatic renewal attempts, then the web server must reload; Day 69, A monitor should shout here: 21 days remaining; Day 60 to 90, Slack for a failed renewal to be found and fixed; Day 90, Expiry. Every browser shows a full-page warning. Renewal at day 60 leaves thirty days for a broken cron job to be noticed. Since mid-2025 nothing emails you, so the thing that notices has to be a monitor you set up."}},"blocks":[{"t":"h2","id":"what-a-certificate-actually-claims","runs":[{"text":"What a certificate actually claims"}]},{"t":"p","runs":[{"text":"A TLS certificate says one thing: at the moment this was issued, whoever asked for it could prove they controlled this domain name."}]},{"t":"p","runs":[{"text":"That is the whole claim. It does not say the company is real, registered, honest, or the one you meant to visit. A domain-validated certificate is free and takes about thirty seconds, which means anyone who can register "},{"text":"add4ly-login.com","code":true},{"text":" can put a padlock on it. Most phishing pages served today are on HTTPS. The padlock means the connection is private; it says nothing about who is on the other end."}]},{"t":"p","runs":[{"text":"What you get from it is still worth having, and it is not optional. Without TLS, everything is readable and editable in transit: passwords, session cookies, the page itself. Public wifi and hostile ISPs have both injected ads into plain HTTP pages, at scale. Browsers now mark HTTP as not secure, HTTP/2 in practice requires TLS, and a payment provider will not talk to you without it."}]},{"t":"h2","id":"getting-one-for-nothing","runs":[{"text":"Getting one for nothing"}]},{"t":"p","runs":[{"text":"Let’s Encrypt issues certificates free, through an automated protocol called ACME. Practically every tool speaks it."}]},{"t":"ul","items":[[{"text":"Caddy","b":true},{"text":" does it with no configuration at all beyond your domain name. Free."}],[{"text":"Certbot","b":true},{"text":" does it for nginx and Apache, and writes the config for you. Free."}],[{"text":"Any managed platform (Cloudflare, Vercel, Fly, Netlify, Render) does it for you and you never see it."}]]},{"t":"p","runs":[{"text":"Paid DV certificates from a reseller cost roughly $10 to $70 a year and give you the same padlock, plus a warranty that nobody has ever successfully claimed. Extended Validation certificates used to show the company name in the URL bar, which was the one thing you were buying; Chrome and Firefox stopped displaying it in 2019. There is no remaining reason for a small site to pay."}]},{"t":"h2","id":"ninety-days-and-the-renewal-is-the-outage","runs":[{"text":"Ninety days, and the renewal is the outage"}]},{"t":"p","runs":[{"text":"Let’s Encrypt certificates last 90 days. The convention is to renew at 60, leaving 30 days of slack for a failing cron job to be noticed."}]},{"t":"p","runs":[{"text":"This is where certificates actually take sites down. Not attacks: expiry. And the safety net you may be remembering is gone: Let’s Encrypt stopped sending expiry-warning emails in mid-2025. Nothing will tell you. Shorter-lived certificates, measured in days rather than months, are being rolled out across the industry, which makes automatic renewal mandatory rather than merely sensible."}]},{"t":"p","runs":[{"text":"Check any site’s dates from a terminal:"}]},{"t":"code","lang":"bash","label":"Bash","text":"echo | openssl s_client -connect addaly.com:443 -servername addaly.com 2>/dev/null \\\n  | openssl x509 -noout -dates -subject"},{"t":"p","runs":[{"text":"Put that in a monitor that shouts at 21 days remaining. It is five minutes of work against the single most common self-inflicted outage in this course."}]},{"t":"h2","id":"the-two-ways-of-proving-you-own-the-name","runs":[{"text":"The two ways of proving you own the name"}]},{"t":"ul","items":[[{"text":"HTTP-01.","b":true},{"text":" The issuer asks for a file at "},{"text":"/.well-known/acme-challenge/<token>","code":true},{"text":" on port 80. Simple, and the default. It needs port 80 reachable from the internet, and it cannot issue wildcards."}],[{"text":"DNS-01.","b":true},{"text":" You publish a TXT record at "},{"text":"_acme-challenge.example.com","code":true},{"text":". This works for wildcard certificates, works when the machine is not publicly reachable, and needs API access to your DNS provider."}]]},{"t":"p","runs":[{"text":"If your renewal keeps failing, it is nearly always one of these two preconditions quietly changing: a firewall rule that closed port 80, or a DNS API token that expired."}]},{"t":"h2","id":"the-failures-you-will-actually-meet","runs":[{"text":"The failures you will actually meet"}]},{"t":"ul","items":[[{"text":"The certificate covers ","b":true},{"text":"example.com","b":true,"code":true},{"text":" but not ","b":true},{"text":"www.example.com","b":true,"code":true},{"text":".","b":true},{"text":" They are two names. Both must be on the certificate, or the visitor who types "},{"text":"www","code":true},{"text":" gets a full-page security warning."}],[{"text":"A missing intermediate chain.","b":true},{"text":" Your browser looks fine, because browsers cache intermediate certificates from other sites you have visited. "},{"text":"curl","code":true},{"text":", Android, and your payment provider’s webhook do not. This is why “it works in Chrome” is not a test. Use "},{"text":"openssl s_client","code":true},{"text":" or SSL Labs."}],[{"text":"The renewal ran but nothing reloaded.","b":true},{"text":" New certificate on disk, old certificate in memory. Your renewal hook must reload the web server, and the cron user must have permission to do it."}],[{"text":"Rate limits.","b":true},{"text":" Testing your automation against the production issuer will lock you out for a week. Use the staging endpoint while you are getting it working."}],[{"text":"Clock skew.","b":true},{"text":" A server whose clock is wrong will reject a perfectly good certificate as not yet valid."}]]},{"t":"h2","id":"hsts-the-header-you-cannot-take-back","runs":[{"text":"HSTS: the header you cannot take back"}]},{"t":"p","runs":[{"text":"Strict-Transport-Security","code":true},{"text":" tells browsers to refuse plain HTTP for your domain for a stated period."}]},{"t":"code","lang":"","label":"Text","text":"Strict-Transport-Security: max-age=31536000; includeSubDomains"},{"t":"p","runs":[{"text":"That is a year. Every browser that has seen it will refuse to load your site over HTTP, and there is no way to reach those browsers and change your mind. If a subdomain of yours is HTTP-only, "},{"text":"includeSubDomains","code":true},{"text":" takes it offline."}]},{"t":"p","runs":[{"text":"Start at "},{"text":"max-age=300","code":true},{"text":", confirm nothing breaks, then raise it in steps. The HSTS preload list, which bakes your domain into the browsers themselves, is close to permanent: removal takes months to reach users. Do not submit a domain you are still experimenting with."}]},{"t":"h2","id":"mixed-content","runs":[{"text":"Mixed content"}]},{"t":"p","runs":[{"text":"One "},{"text":"http://","code":true},{"text":" image URL on an HTTPS page and the browser blocks it silently. Grep your templates and your database content for "},{"text":"http://","code":true},{"text":" before launch, and again after anyone pastes a URL into a CMS field."}]}],"check":{"question":[{"text":"Your certificate renews automatically at 60 days and the cron job has been reporting success for months. One morning the site shows an expired-certificate warning to every visitor, and the certificate file on disk has a valid date. What is the most likely cause?"}],"options":[{"text":[{"text":"The certificate was issued for the apex domain only, so visitors typing www see a name-mismatch warning"}],"correct":false,"tempting":[{"text":"Confuses a name mismatch with an expiry warning; both are red pages, but the browser names which one it is, and a mismatch would have been failing since day one."}]},{"text":[{"text":"Let’s Encrypt revoked the certificate, which browsers checked before displaying the page"}],"correct":false,"tempting":[{"text":"Assumes browsers reliably perform live revocation checks. Most do not: revocation is the weakest part of the system, and it would not present as expiry."}]},{"text":[{"text":"The renewal wrote a new certificate but the web server was never reloaded, so it is still serving the old one from memory"}],"correct":true,"tempting":null},{"text":[{"text":"The clock on the server drifted, so the new certificate is being treated as not yet valid"}],"correct":false,"tempting":[{"text":"A plausible cousin, but clock skew produces a “not yet valid” error and would also break outgoing TLS calls from the box, which you would have noticed elsewhere."}]}],"order":[0,1,3,2],"why":null},"prev":{"slug":"http-and-status-codes","title":"What a status code is telling you","minutes":7},"next":{"slug":"behind-the-proxy","title":"What sits in front of your app","minutes":9}}}