App outage after expired cloud certificate or key: diagnose and fix
For customers whose app suddenly stopped working because a cloud certificate or key expired. This runbook helps you quickly identify whether the failure is TLS, a load balancer certificate, a database/client certificate, or an API/service-account key, then apply the exact fix and verify recovery.
TL;DR — If your app broke right after a certificate or key expired, the fastest first check is the provider dashboard for your domain, load balancer, database, and secrets. The most common fix is to renew or replace an expired TLS certificate (encryption identity for HTTPS) or API key, then restart/redeploy the app so it starts using the new value. Reading time: ~6 min
The scenario
It is a normal Tuesday afternoon. You did not deploy anything, but your site suddenly starts showing a browser security warning, or your app begins returning 502/503 errors even though the hosting dashboard still says the service is running. Login may have stopped working, background jobs are failing, and a payment or email integration that worked yesterday now returns "unauthorized". The common thread: a cloud-managed certificate or key quietly hit its expiration date, and now one link in the chain no longer trusts the other.
Symptoms
- Browser shows certificate warnings such as:
Your connection is not private NET::ERR_CERT_DATE_INVALID SSL certificate problem: certificate has expired - API requests fail with HTTPS/TLS errors:
x509: certificate has expired or is not yet valid SSL routines:ssl3_get_server_certificate:certificate verify failed unable to verify the first certificate - Reverse proxy or load balancer returns:
502 Bad Gateway 525 SSL handshake failed 526 Invalid SSL certificate - App logs show database or upstream connection failures:
FATAL: certificate has expired tls: failed to verify certificate: x509: certificate has expired - Third-party integrations start failing with auth errors:
401 Unauthorized invalid_client invalid_grant Signature has expired ExpiredToken - Users can load the homepage, but login, checkout, email sending, file uploads, or background sync suddenly stop.
Likely causes
| Cause | How common | Quick check |
|---|---|---|
| Public HTTPS/TLS certificate on your domain or load balancer expired | Very common | Open your site in a browser and click the padlock/certificate details, or run `openssl s_client -connect yourdomain.com:443 -servername yourdomain.com </dev/null 2>/dev/null |
| App is using an expired API key, OAuth client secret, or service-account key | Very common | In your provider dashboard, open the app's configured secrets/credentials and check the expiration date on the key in use |
| Database/client certificate expired (app to Postgres/MySQL/Redis) | Common | Check app logs for certificate has expired on DB connections, or inspect the DB/client cert expiry in the DB dashboard |
| Intermediate/chain certificate missing after renewal | Less common | Run openssl s_client -connect yourdomain.com:443 -servername yourdomain.com -showcerts </dev/null |
| System clock drift on server/container causing false "expired" or "not yet valid" errors | Less common | In your provider dashboard, check instance/container time settings, or run date -u |
Step-by-step diagnosis
-
Check what users see in the browser.
- Action: open
https://yourdomain.comin a private/incognito window. - If you see
NET::ERR_CERT_DATE_INVALID,Your connection is not private, or a certificate warning, jump to Fixes → Public HTTPS/TLS certificate on your domain or load balancer expired. - If the homepage loads but actions like login or checkout fail, continue to step 2.
- Action: open
-
Check your provider dashboard for certificate or secret expiry.
- Action: in your provider's dashboard, look in the places that usually hold expiring items: domain/custom hostname settings, load balancer/listener TLS settings, managed database connection security, and app secrets/environment variables (e.g. your provider's dashboard → app/service settings → environment variables or secrets).
- If you find a certificate/key marked expired, expiring today, or revoked, jump to the matching fix section below.
- If nothing obvious is expired, continue to step 3.
-
Test the public certificate from outside.
- Action:
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com </dev/null 2>/dev/null | openssl x509 -noout -dates -issuer -subject- This is your problem if
notAfter=is in the past, or if the command fails to return a valid certificate. - If expired, jump to Fixes → Public HTTPS/TLS certificate on your domain or load balancer expired.
- If dates look valid, continue to step 4.
-
Check whether the certificate chain is incomplete.
- Action:
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com -showcerts </dev/null- This is your problem if verification errors mention
unable to get local issuer certificateor only the leaf certificate is shown without intermediates. - Jump to Fixes → Intermediate/chain certificate missing after renewal.
-
Check app logs for the exact failing dependency.
- Action: in your provider dashboard, open your app/service logs and search for:
certificate has expired x509 invalid_client ExpiredToken Signature has expired- If logs mention Postgres/MySQL/Redis or another internal service, jump to Fixes → Database/client certificate expired.
- If logs mention OAuth, SMTP, storage, cloud API, or
401 Unauthorized, jump to Fixes → App is using an expired API key, OAuth client secret, or service-account key. - If logs mention
not yet validas well as expiry, continue to step 6.
-
Check server/container time.
- Action:
date -u- Compare the result to current UTC time on your phone or a trusted clock.
- This is your problem if the server time is off by more than a minute or two, especially if errors say
certificate is not yet valid. - Jump to Fixes → System clock drift causing false certificate expiry errors.
View as text
[User Browser] ---- 1. HTTPS ----> [Load Balancer/CDN]
|
| 2. TLS cert check
v
[App Service]
|
| 3. DB/API auth
v
[Database / Third-party API]Fixes
Public HTTPS/TLS certificate on your domain or load balancer expired
- Dashboard path: in your provider dashboard, open the domain/custom hostname or load balancer HTTPS/TLS settings and reissue/renew the certificate. If the UI offers "Retry validation" or "Reissue certificate", use that first.
- If you manage nginx yourself, replace the cert files and reload nginx:
sudo cp fullchain.pem /etc/ssl/yourapp/fullchain.pem sudo cp privkey.pem /etc/ssl/yourapp/privkey.pem sudo nginx -t && sudo systemctl reload nginx - Example nginx config should point to the full chain, not only the leaf cert:
server { listen 443 ssl http2; server_name yourdomain.com; ssl_certificate /etc/ssl/yourapp/fullchain.pem; ssl_certificate_key /etc/ssl/yourapp/privkey.pem; } - If DNS validation failed during renewal, fix the requested TXT/CNAME record in your DNS dashboard (e.g. your DNS provider → DNS/Records), wait for propagation, then click reissue again.
- Verify it worked:
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com </dev/null 2>/dev/null | openssl x509 -noout -dates
App is using an expired API key, OAuth client secret, or service-account key
- Dashboard path: open the third-party provider that the app talks to, create a new key/secret, then copy it into your app's secrets/environment variables in your hosting dashboard.
- Common pattern for app config:
API_KEY=replace_with_new_value OAUTH_CLIENT_SECRET=replace_with_new_value GOOGLE_APPLICATION_CREDENTIALS_JSON={...new-json...} - After updating the secret, redeploy or restart the app so it loads the new value.
- If you have shell access and use a
.envfile:⚠️ Restarting or redeploying the app can cause brief downtime.
nano .env # replace the expired value systemctl restart your-app - Verify it worked: retry the failing action and confirm logs no longer show
401 Unauthorized,invalid_client, orExpiredToken.
Database/client certificate expired
- Dashboard path: in your managed database dashboard, rotate or download a fresh client certificate/CA bundle, then update the app's DB SSL settings.
- Typical environment variables:
DB_SSLMODE=verify-full DB_SSLROOTCERT=/app/certs/ca.pem DB_SSLCERT=/app/certs/client.crt DB_SSLKEY=/app/certs/client.key - If your provider rotated the CA (certificate authority), replace the CA file your app trusts, not just the client cert.
- For Postgres connection strings, a corrected example looks like:
postgres://appuser:password@db.example.com:5432/appdb?sslmode=verify-full&sslrootcert=/app/certs/ca.pem - Restart/redeploy the app after replacing the files or variables.
- Verify it worked: app logs stop showing
FATAL: certificate has expired, and database-backed pages load normally.
Intermediate/chain certificate missing after renewal
- Fix your web server or load balancer to present the full chain certificate file, not only the site certificate.
- For nginx:
ssl_certificate /etc/ssl/yourapp/fullchain.pem; ssl_certificate_key /etc/ssl/yourapp/privkey.pem; - Then test and reload:
sudo nginx -t && sudo systemctl reload nginx - If you upload certificates in a dashboard, upload the combined certificate bundle that includes intermediates, usually called
fullchain.pem. - Verify it worked:
The verification errors should be gone.
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com -showcerts </dev/null
System clock drift causing false certificate expiry errors
- Dashboard path: if your provider offers instance time sync controls, enable automatic time sync/NTP (network time protocol).
- On a Linux server with shell access:
timedatectl status sudo timedatectl set-ntp true timedatectl status - In containers, fix the host/node time; containers usually inherit it.
- After correcting time, restart the affected app process if it cached failed TLS sessions.
- Verify it worked:
Then retry the failing request and confirm
date -unot yet validor expiry errors are gone.
Prevention
- Add an external certificate expiry check for every public hostname. Example cron job from any monitoring box:
Alert if expiry is under 21 days.
echo | openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null | openssl x509 -noout -enddate - Add a startup check in your app deploy pipeline for required secrets and cert files:
test -n "$API_KEY" && test -f /app/certs/ca.pem || exit 1 - Track secret expiry dates in one place. Even a simple JSON file in your repo is better than memory:
Then have a scheduled job alert 30 days before each date.
{ "payment_api_key_expires": "2026-11-15", "smtp_client_secret_expires": "2026-12-01", "db_client_cert_expires": "2027-01-10" } - Prefer provider-managed certificate renewal where available, and keep DNS validation records in place so renewals do not fail silently.
- Add a smoke test after every deploy and every nightly schedule that exercises login, a DB query, and one third-party API call. Example:
curl -fsS https://yourdomain.com/healthz && curl -fsS https://yourdomain.com/login - If you use your own nginx or app server, store
fullchain.pemandprivkey.pempaths in config management, and run a config test before reload:nginx -t
This article was written by an AI system and published pending human review. Verify anything you intend to act on.
Have a project in mind?
Get an instant AI price estimate for it, or talk directly to our team.
One email a month on what we learn building with AI