You renewed the SSL certificate but the browser still shows the old one
· tls · ssl · certificates · nginx · openssl
tlssslcertificatesnginxopensslYou installed the new certificate, but clients still see the old, expired one. In almost every case the web server is still serving the old cert from memory because it was never reloaded after the new file was written to disk. The fix is usually one command — systemctl reload nginx (or apachectl graceful) — followed by verifying the live cert with openssl s_client rather than trusting the file on disk. If a reload does not fix it, something between you and the client (a load balancer, a CDN, or the wrong vhost) is serving a different cert than the one you replaced.
Verify what is actually served, not what is on disk
Stop looking at the .pem file. Ask the running server what it is presenting on the wire:
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -dates -subject -issuer
The -servername flag is mandatory on any host with multiple certificates — it sends the SNI hostname so you get the right vhost instead of the default cert. Read the notAfter date. If it still shows the old expiry, the new cert is not live yet, full stop. This is the single most useful command in this whole article; the browser padlock and the file timestamp both lie to you.
The web server was never reloaded
nginx and Apache load certificate files once at startup and hold them in memory. Writing a new file to /etc/letsencrypt/live/... or /etc/ssl/ changes nothing until the process re-reads it. A graceful reload is enough — no full restart, no dropped connections:
nginx -t && systemctl reload nginx # nginx
apachectl configtest && apachectl graceful # apache
This is the number-one cause. Certbot's --deploy-hook exists precisely so the renewal triggers a reload automatically; if your renewal cron has no deploy hook, you will hit this every 90 days.
You fixed one node, not all of them
If a reverse proxy or load balancer terminates TLS, the cert lives on it, not your origin — and a multi-node pool means several copies. Reloading one backend while the LB still holds the old cert changes nothing the client sees. Hit each node directly:
openssl s_client -connect 10.0.0.11:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -enddate
Loop over every IP in the pool and every SNI hostname the box answers for. A wildcard plus three apex names is four certs to check, not one.
A CDN or Cloudflare edge is caching the old cert
If Cloudflare, Fastly, or any CDN terminates TLS at the edge, the client sees the edge certificate, and your origin renewal is irrelevant until the edge updates. For managed edge certs this is automatic but lagging; for custom-uploaded certs you must upload the new one to the edge and purge. The give-away: openssl s_client against your origin IP shows the new date, but against the public hostname shows the old one.
Wrong path, wrong vhost, or a stale chain
Three subtler causes. The cert was written to a path the config does not reference, so the server kept the old file. The new cert was installed under the wrong server_name/vhost, so connections fall through to the default cert. Or the leaf renewed but the intermediate fullchain did not, and clients reject the broken chain. Confirm the chain end to end:
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null
You should see the leaf and every intermediate up to a trusted root.
When the server is fine but your browser still shows old
If openssl s_client reports the new date but your browser still shows the old, expired cert, the stale copy is local. Browsers and the OS cache certificates and TLS sessions; a hard refresh, a fresh private window, or clearing the TLS state (Chrome's chrome://net-internals clear-cache, or restarting the browser) makes the client re-fetch the leaf. Trust the openssl result — if the wire shows the new notAfter, the server is done and the problem is on your end.
Further reading
- How TLS certificates work and how to read one
- RFC 6066 — TLS Extensions (Server Name Indication)