Skip to content

Guides · Certificates

Renewed but not served: check the certificate each endpoint presents

Certbot says renewal worked, but the server still serves the old cert. Why deploy hooks, reloads and Cloudflare origins fail, and how to check.

Updated 14 November 2026 · 8 min read

Most certificate monitoring answers one question: did the renewal job succeed? That is the wrong question. Users don't connect to your renewal log. They connect to a listener on a load balancer, an origin behind a CDN, or a box someone set up three years ago. What matters is the certificate that endpoint actually serves.

This post covers the common ways a renewal "succeeds" while users still get the old certificate, plus a short checklist for catching it. If the certificate was issued but never imported onto an appliance or host, see Certificate renewed but not installed.

The gap between "renewed" and "served"

Renewal and deployment are two separate steps. A certificate can exist on disk, in a secrets store, or in your CA account without ever reaching the process that terminates TLS. Every hop in between can fail without anyone noticing:

  • The new files are written, but the web server is never reloaded.
  • The reload runs, but the new config is rejected and the old one keeps running.
  • One node in a pool gets the new cert and the others don't.
  • The edge (CDN) certificate is fine, but the origin certificate behind it has expired.

Shorter certificate lifetimes mean this gap shows up more often. Under the CA/Browser Forum Baseline Requirements (section 6.3.2, added by Ballot SC-081v3), publicly trusted TLS certificates issued on or after 2026-03-15 may be valid for at most 200 days. The cap drops to 100 days for certificates issued on or after 2027-03-15, and to 47 days on or after 2029-03-15 (BR text on GitHub). More renewals per year means more chances for a deployment step to break.

Failure mode 1: certbot renewed, the hook didn't run

On many Linux servers, certbot renews certificates and a deploy hook reloads nginx or Apache. The certbot documentation is clear about when deploy hooks run: --deploy-hook commands (and executables in /etc/letsencrypt/renewal-hooks/deploy) run only after a successful renewal.

The catch is the test you probably rely on. Per the same docs, during --dry-run the --pre-hook and --post-hook commands run by default, but "--deploy-hook commands do not run, unless enabled by --run-deploy-hooks." Amit Shukla put it plainly on X: a passing certbot renew --dry-run doesn't test your reload, so a broken hook stays hidden, "the cert renews on disk, and the server keeps serving the old one."

The docs add two more details that matter here:

  • certbot renew exits with 0 both when it renewed something and when nothing needed renewing. An exit code of 0 doesn't tell you a new cert is live.
  • --dry-run uses the Let's Encrypt staging server by default, so it tests issuance against staging, not your production deployment path.

Fix: test the hook with certbot renew --dry-run --run-deploy-hooks (the flag is in the current docs, version 5.9.0; check certbot --help all on your version), and verify the result from outside the box (see the checklist below).

Failure mode 2: the reload ran, the old config kept running

Reloading a server is not the same as loading the new certificate. nginx is a good example. Per the nginx control docs, on HUP the master process checks the syntax and tries to apply the new configuration. "If this fails, it rolls back changes and continues to work with old configuration."

That is the right behavior for uptime. It also means a bad path, a key/cert mismatch or a permissions problem can leave the old certificate in service while your hook script reports success. Make the hook fail loudly: run nginx -t first, check the exit code, then compare the served fingerprint with the file on disk.

Fleets add another version of this problem. A config management run, a container image or a secrets sync may update some nodes and miss others. If you only check the hostname, you see whichever node the load balancer gives you that time.

Failure mode 3: CDN edge is fine, origin is not

With a CDN in front, there are two certificates: the one the CDN serves to browsers (edge) and the one your origin serves to the CDN. Checking the public hostname only shows you the edge.

Cloudflare documents what happens when the origin side breaks. Error 526 means Cloudflare cannot validate the origin's certificate while the SSL mode is Full (strict). Listed causes include an expired or revoked certificate, a hostname missing from the CN/SAN, and an incomplete chain.

The cost of missing this can be real. On November 7, 2026, Mubashir Shah wrote on X that his SSL certificate expired on October 27, that "visitors were getting a cloudflare error for days," and that he didn't notice for four or five days because he was watching traffic, not whether the site worked. He reports that some top pages were deindexed and others fell from #1 to page 4 or 5. His Search Console screenshot shows the drop. These are his own reported figures and I haven't verified them. His post also doesn't say which certificate (edge or origin) expired or which error code visitors saw.

His summary is the useful part: "i was checking how many people showed up, not whether they could actually use the site."

A public example: PayPal's status page

On November 8, 2026 (03:10 UTC), Hrishikesh Barua posted on X that PayPal's status page had an expired certificate, with a screenshot of the certificate for www.paypal-status.com.

I checked the live endpoints with openssl s_client on November 9, 2026 at 05:14 UTC, from one network location:

  • www.paypal-status.com and paypal-status.com both served a DigiCert EV certificate valid from 2026-04-23 to 2026-11-07 23:59:59 GMT. openssl reported certificate has expired.
  • status.paypal.com served a different certificate from the same CA, valid from 2026-10-14 to 2027-04-30, whose SAN list also includes www.paypal-status.com and paypal-status.com.
  • A crt.sh search for paypal-status.com at the same time did not list the newer certificate. CT search services index on different schedules, so this doesn't prove anything either way.

So a certificate covering the expired hostname existed and was being served elsewhere, while that hostname still served the expired one. That fits the pattern this article is about, but from outside I can't tell why. It could be a deployment gap, separate infrastructure for each hostname, or something else. PayPal hasn't said, and the cause is unknown.

Checklist: verify what each endpoint serves

1. List every endpoint, not just every hostname. That means each load-balancer listener, each node IP, each origin behind a CDN, and non-443 ports (SMTP, IMAP, admin panels).

2. Compare the served leaf fingerprint with the cert you expect. Point -connect at a specific IP and keep -servername set to the hostname so SNI is right:

# What this endpoint actually serves
echo | openssl s_client -connect 203.0.113.10:443 -servername www.example.com 2>/dev/null \
  | openssl x509 -noout -fingerprint -sha256 -enddate -subject

# What you expect it to serve
openssl x509 -in /etc/letsencrypt/live/www.example.com/cert.pem -noout -fingerprint -sha256 -enddate

If the fingerprints differ after a renewal, the new cert isn't live on that endpoint.

3. Check the origin directly when you use a CDN. Run the same command against the origin IP, or use curl --resolve www.example.com:443:ORIGIN_IP https://www.example.com/. Origin certs need expiry tracking of their own.

4. Alert on non-200, not just on expiry. A cert check can pass while users get a 5xx from the CDN:

code=$(curl -sS -o /dev/null -w '%{http_code}' https://www.example.com/healthz)
[ "$code" = "200" ] || echo "ALERT: www.example.com returned $code"

5. Test the hook, not just the renewal. Use certbot renew --dry-run --run-deploy-hooks, make the hook exit non-zero if the config test or reload fails, and run checks from outside the server.

6. Alert on "renewed but not served." If CT logs or your CA show a newer certificate for a hostname, but an endpoint still serves the older one some hours later, flag it. Don't wait for the old certificate's expiry date.

FAQ

Why is my server still showing the old SSL certificate after renewal? Usually the web server never loaded the new files. The deploy hook didn't run or failed, the reload was rejected and the old config kept running, or only some nodes were updated. Compare the served fingerprint with the file on disk.

Does certbot renew --dry-run test my deploy hook? Not by default. Per the certbot docs, deploy hooks are skipped during --dry-run unless you add --run-deploy-hooks.

What does Cloudflare error 526 mean? Cloudflare couldn't validate your origin server's certificate (for example, because it expired) while SSL mode is Full (strict). Your edge certificate can be perfectly valid at the same time.

A note on Nameward

I'm building Nameward, a certificate and domain monitoring tool for MSPs and ops/security teams, to do this kind of check: inventory certificates from CT logs, then confirm what each endpoint actually serves, so "renewed but not served" shows up as its own alert. You can sign up at nameward.app. If you'd like to join the beta or tell me how this breaks in your environment, get in touch.

Sources (accessed November 9, 2026)

Free scan

No signup. Only scan domains you own or manage.

More in Certificates