NGINX Continues Serving a Pre-Renewal TLS Certificate After Renewal
Confirm that automated certificate renewal succeeded on disk, validate NGINX configuration, and reload it safely so the renewed TLS certificate is served.
Destructive Operation
This intervention contains destructive operations. Proceed with extreme caution and ensure database backups exist before execution.
01 // Diagnose
Symptom
Clients receive the pre-renewal or expired certificate for an HTTPS hostname even though the ACME client reports that a newer certificate was successfully installed at the configured live path.
Detection Signature
-
From an external network, run
openssl s_client -connect <HOSTNAME>:443 -servername <HOSTNAME> </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -serial -dates -fingerprint -sha256and record the served certificate. -
On the server, run
sudo nginx -Tand identify the exactssl_certificateandssl_certificate_keypaths for the matching server_name without copying key material. -
Run
sudo certbot certificatesand compare the intended lineage, domains, expiry, and live certificate path. -
Compare the served and on-disk certificate SHA-256 fingerprints.
-
Check renewal and NGINX logs for a successful issuance followed by a failed deploy hook, configuration test, or reload.
-
Verify DNS sends the affected hostname to this load balancer or server and check every active endpoint when traffic is distributed.
Root Cause Analysis
-
A renewed certificate exists at the NGINX-configured path, but the running NGINX workers still hold the pre-renewal certificate because the renewal deploy hook did not complete a successful reload.
-
Renewal on disk and activation in the running service are separate operational steps.
02 // Contain & Prevent
Blast Radius
Browsers, APIs, agents, and service-to-service clients can reject the TLS connection.
Only some users may fail when one node or load balancer in a pool serves the expired certificate.
An incorrect certificate-path change can break NGINX reload or expose the wrong hostname certificate.
Prevention Measures
-
Run a post-renewal configuration test and controlled NGINX reload through the ACME deploy hook.
-
Monitor the certificate served externally at every endpoint rather than only the local certificate file.
-
Alert well before expiry and after any mismatch between served and expected fingerprints.
-
Keep certificate lineage and server_name ownership documented and reviewed.
03 // Fix & Intervention
Pre-Flight Checks
-
Confirm the current hostname and server identity, prove this is an endpoint that serves
<HOSTNAME>, and identify every other endpoint serving it. -
Run
sudo -vandsudo -l, then confirm the listed policy explicitly authorizes the approved absolute NGINX binary for configuration testing and reload on this instance. -
Record the currently served certificate fingerprint and use
sudo nginx -Tto resolve the exact certificate path for the matching server_name. -
Calculate and record the on-disk fingerprint at that exact configured path; hard-stop unless it differs from the served fingerprint, covers
<HOSTNAME>, has a later valid expiry, and matches its configured private key. -
Confirm the ACME account, renewal configuration, challenge path, and intended certificate lineage.
-
Confirm a previous still-valid certificate or alternate healthy endpoint exists for rollback; never plan to restore an expired certificate.
-
Obtain incident commander approval and require a fresh successful
sudo nginx -timmediately before reload; abort without reloading if that test fails.
Execution CommandsCOMMANDS
set -e; sudo systemctl status nginx --no-pager; SERVED_CERT=$(mktemp); trap 'rm -f "$SERVED_CERT"' EXIT; openssl s_client -connect <HOSTNAME>:443 -servername <HOSTNAME> -showcerts </dev/null > "$SERVED_CERT" 2>/dev/null; SERVED_FP=$(openssl x509 -in "$SERVED_CERT" -noout -fingerprint -sha256); DISK_FP=$(sudo openssl x509 -in <NGINX_CERTIFICATE_PATH> -noout -fingerprint -sha256); printf 'served=%s
on_disk=%s
' "$SERVED_FP" "$DISK_FP"; test -n "$SERVED_FP"; test -n "$DISK_FP"; if test "$SERVED_FP" = "$DISK_FP"; then echo 'ABORT: served and on-disk fingerprints are identical'; exit 1; fi; sudo nginx -t; sudo nginx -s reload; openssl s_client -connect <HOSTNAME>:443 -servername <HOSTNAME> </dev/null 2>/dev/null | openssl x509 -noout -serial -dates -fingerprint -sha256
04 // Verify & Recover
Verification Steps
-
Repeat the external OpenSSL check and confirm the served fingerprint, subject alternative names, issuer, notBefore, and notAfter match the intended renewed certificate.
-
Test the hostname from at least two independent network paths and every load-balanced endpoint.
-
Confirm
sudo nginx -tstill succeeds and NGINX logs contain no reload or key-loading error. -
Confirm the application returns its expected HTTPS health response.
-
Monitor handshake failures and certificate-expiry alerts for at least 15 minutes.
Rollback Protocol
-
If NGINX cannot load the renewed certificate, do not restore an expired certificate.
-
Keep or return traffic to the previously captured still-valid certificate or alternate healthy endpoint, restore the captured NGINX certificate paths if they referenced that valid certificate, run
sudo nginx -t, reload, and escalate the issuance or key-pair problem. -
If no valid certificate remains, keep the affected endpoint out of service rather than bypassing TLS validation.
Escalation
-
The certificate private key does not match the renewed certificate.
-
DNS or a load balancer directs traffic to an unmanaged endpoint.
-
ACME validation cannot complete before the recovery objective.
-
The only available rollback certificate is expired, revoked, compromised, or does not cover the hostname.
Authoritative Sources
NGINX command-line parameters
NGINX configuration testing and graceful configuration reload behavior.
Configuring HTTPS servers
NGINX TLS certificate and private-key configuration paths and HTTPS server behavior.
Let's Encrypt certificate profiles
Certificate validity periods, identifier coverage, and certificate profile behavior.
Certbot: Renewing certificates
Certbot renewal behavior, certificate lineages, renewal hooks, and deploy hooks.
openssl-s_client
Connecting to a TLS endpoint with an explicit server name to inspect the certificate served over the network.
openssl-x509
Printing certificate serial numbers, validity dates, subject names, and fingerprints for deterministic comparison.