How Dangerous Is an HTTPS Certificate Expiration and How Ops Can Prevent It?
When an HTTPS certificate expires, browsers show warnings, users abandon sites, services become unavailable, and security is weakened, so this article explains the TLS fundamentals, the risks of expiration, real‑world outage cases, and provides step‑by‑step guidance on acquisition, deployment, automated renewal, monitoring, and best‑practice procedures for reliable certificate management.
Background and Problem
HTTPS certificate expiration can trigger browser security warnings, cause users to abandon the site, and in some cases make the service completely unavailable.
HTTPS Basics
Encryption Principle
HTTPS secures HTTP traffic with TLS. TLS 1.3 (released 2018) provides performance and security improvements over TLS 1.2.
Certificate Structure
Subject (domain, organization)
Issuer (CA information)
Public key
Validity period (Not Before / Not After)
Serial number
Signature
# View certificate details
openssl x509 -in certificate.crt -text -noout
# Example output (truncated)
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 04:cd:3a:5b:f2:7c:8a:3e:9d:5e:8a:3c:1d:4e:8a:2b:3c
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = US, O = Let's Encrypt, CN = R3
Validity
Not Before: Jan 15 00:00:00 2026 GMT
Not After : Apr 15 00:00:00 2026 GMT
Subject: CN = example.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)Certificate Chain
Browsers verify the end‑entity certificate, any intermediate certificates, and a trusted root certificate.
# Verify chain locally
openssl verify -CAfile chain.crt certificate.crt
# Show full chain from a remote server
openssl s_client -connect example.com:443 -showcertsRisks of Expiration
Service Unavailability
Browsers display a security warning page.
Users must manually confirm; most close the page.
Mobile app APIs fail, breaking app functionality.
Data Security Risks
Expired certificates no longer provide full encryption.
Man‑in‑the‑middle attack surface increases.
Compliance frameworks such as PCI DSS require valid certificates.
Operational Pressure
Expiration often occurs outside working hours, generating night‑time alerts.
Emergency roll‑backs must be performed under time pressure.
Manual renewal can be error‑prone when rushed.
Real‑World Outage Example
A commerce platform received massive user complaints at 03:00 AM because its payment API handshake failed due to an expired certificate. The outage lasted two hours and caused losses exceeding one million dollars. The root cause was a manual renewal process without monitoring alerts.
Certificate Management Basics
Acquisition Methods
Commercial CAs (DigiCert, GlobalSign, Entrust) – high trust.
Let’s Encrypt – free, 90‑day certificates, fully automated.
Private CA – for internal services.
Let’s Encrypt Certificate Request
Install certbot
# Ubuntu/Debian
sudo apt update
sudo apt install certbot python3-certbot-nginx
# CentOS/RHEL
sudo dnf install certbot python3-certbot-nginx
# Verify version
certbot --version # certbot 3.0.1Request a Certificate
# Single domain
sudo certbot certonly --nginx -d example.com
# Multiple domains
sudo certbot certonly --nginx -d example.com -d www.example.com
# Webroot mode (requires pre‑configured webroot)
sudo certbot certonly --webroot -w /var/www/html -d example.com -d www.example.com
# DNS‑01 challenge (for environments where HTTP validation is impossible)
sudo certbot certonly --manual --preferred-challenges dns -d example.comCertificate Locations
# List files in the live directory
ls -la /etc/letsencrypt/live/example.com/
# fullchain.pem – server + intermediate
# privkey.pem – private key
# cert.pem – server certificate
# chain.pem – intermediate certificateDeployment
Nginx Configuration
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
}
# Redirect HTTP to HTTPS
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}Apache Configuration
<VirtualHost *:443>
ServerName example.com
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/example.com/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/example.com/chain.pem
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
</VirtualHost>Verification
# Check dates
openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -noout -dates
# Verify chain
openssl verify /etc/letsencrypt/live/example.com/fullchain.pem
# Remote check
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates -issuer -subjectRenewal
Let’s Encrypt certificates are valid for 90 days and must be renewed regularly.
# Renew all certificates
sudo certbot renew
# Renew a specific certificate
sudo certbot renew --cert-name example.com
# Dry‑run (no changes)
sudo certbot renew --dry-run
# Force renewal ignoring expiry
sudo certbot renew --force-renewalAutomated Certificate Management
certbot Auto‑Renew Mechanism
After installation certbot creates a systemd timer that runs twice daily.
# List timers
sudo systemctl list-timers | grep certbot
# View cron file (fallback)
sudo cat /etc/cron.d/certbot
# 0 */12 * * * root test -x /usr/bin/certbot -a ! -d /run/systemd/system && perl -e 'sleep int(rand(43200))' && certbot -q renew
# Check systemd timer status
sudo systemctl status certbot-renew.timerNginx Auto‑Deployment
The certbot nginx plugin can automatically modify the Nginx configuration during issuance.
# Issue and configure
sudo certbot --nginx -d example.com -d www.example.com
# The plugin performs:
# 1. Domain validation
# 2. Certificate issuance
# 3. Nginx config update
# 4. Renewal hook setupPost‑Renew Reload
After renewal the web server must be reloaded.
# View renewal hook
sudo cat /etc/letsencrypt/renewal/example.com.conf
# Example snippet
[renewalparams]
account = xxxxxxxx
authenticator = nginx
installer = nginx
server = https://acme-v02.api.letsencrypt.org/directory
post_hook = systemctl reload nginxFull Automation Script Example
#!/bin/bash
LOGFILE="/var/log/cert-renewal.log"
CERT_DIR="/etc/letsencrypt/live"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a $LOGFILE
}
log "Starting certificate renewal check..."
sudo certbot renew --quiet --deploy-hook "systemctl reload nginx" 2>&1 | tee -a $LOGFILE
if [ $? -eq 0 ]; then
log "Certificate renewal completed successfully"
for cert in $(ls $CERT_DIR); do
expiry=$(sudo openssl x509 -in $CERT_DIR/$cert/cert.pem -noout -enddate 2>/dev/null | cut -d= -f2)
log "Certificate $cert expires: $expiry"
done
else
log "ERROR: Certificate renewal failed"
# Integration with alerting (e.g., email, Slack) can be added here
fiCertificate Monitoring Solutions
Expiry Monitoring Scripts
Local Script
#!/bin/bash
ALERT_DAYS=30
CERT_FILE="/etc/letsencrypt/live/example.com/cert.pem"
ALERT_EMAIL="[email protected]"
EXPIRY_DATE=$(sudo openssl x509 -in $CERT_FILE -noout -enddate | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY_DATE" +%s)
NOW_EPOCH=$(date +%s)
DAYS_UNTIL_EXPIRY=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
echo "Certificate expires in $DAYS_UNTIL_EXPIRY days (on $EXPIRY_DATE)"
if [ $DAYS_UNTIL_EXPIRY -le $ALERT_DAYS ]; then
echo "WARNING: Certificate will expire soon!" | mail -s "Certificate Expiry Alert" $ALERT_EMAIL
exit 1
fiRemote Check with OpenSSL
#!/bin/bash
DOMAIN=$1
ALERT_DAYS=${2:-30}
EXPIRY_DATE=$(echo | openssl s_client -servername $DOMAIN -connect $DOMAIN:443 2>/dev/null | openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
if [ -z "$EXPIRY_DATE" ]; then
echo "ERROR: Cannot get certificate for $DOMAIN" >&2
exit 1
fi
EXPIRY_EPOCH=$(date -d "$EXPIRY_DATE" +%s)
NOW_EPOCH=$(date +%s)
DAYS_UNTIL_EXPIRY=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
echo "$DOMAIN: expires in $DAYS_UNTIL_EXPIRY days (on $EXPIRY_DATE)"
if [ $DAYS_UNTIL_EXPIRY -le $ALERT_DAYS ]; then
echo "ALERT: $DOMAIN certificate will expire in $DAYS_UNTIL_EXPIRY days" >&2
exit 1
fiPrometheus Monitoring
Use an SSL exporter to expose certificate expiry metrics.
# prometheus.yml snippet
scrape_configs:
- job_name: 'ssl-certificate-exporter'
static_configs:
- targets:
- example.com:443
- api.example.com:443
metrics_path: /probe
params:
module: [https_02]
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: ssl-certificate-exporter:9119Run the exporter:
docker run -d \
--name ssl_exporter \
-p 9119:9119 \
prom/blackbox_exporter:latestAlerting rules (expiring soon and expired):
groups:
- name: certificate-expiry
rules:
- alert: CertificateExpiringSoon
expr: ssl_cert_expires_in_seconds < 86400*30
for: 1h
labels:
severity: warning
annotations:
summary: "Certificate {{ $labels.instance }} expiring in less than 30 days"
description: "Certificate expires on {{ $value | humanizeDuration }}"
- alert: CertificateExpired
expr: ssl_cert_expires_in_seconds < 0
for: 5m
labels:
severity: critical
annotations:
summary: "Certificate {{ $labels.instance }} has expired"
description: "Certificate expired on {{ $labels.instance }}"Grafana Dashboard
Key panels display remaining days, percentage of validity, countdown, and domain‑grouped lists.
{
"panels": [
{
"title": "Certificate Expiry",
"type": "stat",
"targets": [{"expr": "ssl_cert_expires_in_seconds / 86400", "legendFormat": "{{ instance }}"}],
"fieldConfig": {"defaults": {"thresholds": {"mode": "absolute", "steps": [{"value": 0, "color": "red"}, {"value": 7, "color": "orange"}, {"value": 30, "color": "green"}]}}}
}
]
}Cloud Provider Monitoring
AWS Certificate Manager – notifications 30/45/60/90 days before expiry.
Alibaba Cloud SSL – expiry reminders.
Tencent Cloud SSL – automatic renewal.
Certificate Management Tools
acme.sh
# Install acme.sh
curl https://get.acme.sh | sh -s [email protected]
# Issue a certificate
.acme.sh/acme.sh --issue -d example.com -d www.example.com --nginx /etc/nginx/nginx.conf
# Install the certificate and reload Nginx
.acme.sh/acme.sh --install-cert -d example.com \
--key-file /etc/nginx/ssl/key.pem \
--fullchain-file /etc/nginx/ssl/fullchain.pem \
--reloadcmd "systemctl reload nginx"Vault PKI
# Enable PKI secrets engine
vault secrets enable pki
# Generate a root CA
vault write pki/root/generate/internal common_name="example.com Internal CA" ttl=87600h
# Create a role for example.com
vault write pki/roles/example-dot-com allowed_domains="example.com" allow_subdomains=true max_ttl=72h
# Issue a certificate
vault read pki/issue/example-dot-com common_name="app.example.com"cert‑manager (Kubernetes)
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: [email protected]
privateKeySecretRef:
name: letsencrypt-prod-account-key
solvers:
- http01:
ingress:
class: nginx
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: example-com-tls
namespace: production
spec:
secretName: example-com-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- example.com
- www.example.com
duration: 2160h # 90 days
renewBefore: 360h # renew 15 days earlyPrivate Certificate Authority
Build a Private CA
# Generate CA private key
openssl genrsa -aes256 -out ca.key 4096
# Create self‑signed CA certificate (10 years)
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \
-out ca.crt -subj "/CN=Internal Root CA/O=Example Inc"
# Distribute CA cert to servers
sudo cp ca.crt /usr/local/share/ca-certificates/internal-ca.crt
sudo update-ca-certificatesIssue Internal Certificates
# Generate service key
openssl genrsa -out internal.key 2048
# Create CSR
openssl req -new -key internal.key -out internal.csr -subj "/CN=internal.example.com/O=Example Inc"
# Sign with private CA
openssl x509 -req -in internal.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out internal.crt -days 365 -sha256
# Verify
openssl verify -CAfile ca.crt internal.crtCommon Issue Handling
Incomplete Certificate Chain
# Wrong: use cert.pem only
ssl_certificate /etc/letsencrypt/live/example.com/cert.pem;
# Correct: use fullchain.pem
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;Private Key Permissions
# Ensure 600 permissions and root ownership
sudo chmod 600 /etc/letsencrypt/live/example.com/privkey.pem
sudo chown root:root /etc/letsencrypt/live/example.com/privkey.pemOCSP Stapling Issues
# Disable temporarily if verification fails
ssl_stapling off;
ssl_stapling_verify off;Renewal Failures
# Dry‑run to see the error
sudo certbot renew --dry-run
# Typical causes and fixes:
# 1. Web server not running – start Nginx
sudo systemctl start nginx
# 2. DNS resolution problems – verify with dig
dig example.com
# 3. Port 80 occupied – free the port or switch to DNS‑01 challenge
# 4. Let's Encrypt rate limits – wait or use --staging for testing
sudo certbot renew --stagingBest Practices
Standardize on Let’s Encrypt unless a special requirement exists.
Set certificate validity to 90 days and schedule renewal 30 days before expiry.
Store certificates under /etc/letsencrypt/live/{domain}/ and keep private keys at permission 600 owned by root.
Configure dual alerts (30 days and 7 days before expiry) that include domain, remaining days, expiry date, and remediation steps.
Automate every step – issuance, deployment, renewal, and post‑renew reload.
Maintain audit logs of certificate actions and perform periodic disaster‑recovery drills.
Prepare a manual renewal playbook and keep backup copies of historic certificates for quick rollback.
Conclusion
HTTPS certificate management is a critical operational responsibility. Expiration can cause user‑facing errors, service outages, and security gaps. Understanding TLS fundamentals, automating acquisition and renewal with tools such as certbot, acme.sh, or cert‑manager, and implementing proactive monitoring and alerting are essential to keep services reliable and secure.
Ops Community
A leading IT operations community where professionals share and grow together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
