certbot renew Failure and NET::ERR_CERT_DATE_INVALID: A 30-Minute Recovery Guide
If a site that was fine yesterday suddenly blocked users overnight with a red warning and NET::ERR_CERT_DATE_INVALID, your Let's Encrypt certificate almost certainly expired and auto-renewal had quietly died. Let's Encrypt certificates last 90 days; once cron or a systemd timer starts failing, expiry creeps up unnoticed until browsers tell your users first.
The good news is that most cases are recoverable in 30 minutes. This post assumes the site is down right now and leads with commands. First we pin the cause from symptoms, then copy-paste mode-specific recovery commands, then permanently revive auto-renewal.
1-minute emergency diagnosis: symptom → cause mapping table
Find the message from the browser or certbot renew output in the table below.
| Visible error message | Actual cause | Immediate check command | Recovery section |
|---|---|---|---|
NET::ERR_CERT_DATE_INVALID (browser) | Certificate already expired | certbot certificates | ⑤ |
Timeout during connect (likely firewall problem) | Port 80 blocked/firewall | ss -tlnp | grep :80 | ① |
Connection refused | Port 80 not bound (web server down) | systemctl status nginx | ① |
Failed authorization procedure ... 404 | webroot path error | Check nginx root directive | ② |
DNS problem: NXDOMAIN looking up A | DNS-01/record issue | dig +short example.com | ③ |
too many certificates already issued for | Rate limit exceeded | (wait or staging) | ④ |
First, check the current certificate status.
# certbot이 관리하는 인증서 목록과 만료일
certbot certificates
# 파일에서 직접 만료일 확인
openssl x509 -enddate -noout -in /etc/letsencrypt/live/example.com/fullchain.pem
# 실제 서비스 중인 인증서를 원격에서 확인
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -datesDetailed diagnosis and recovery for the five failure causes
① Port 80 blocked or occupied (Timeout during connect)
The HTTP-01 challenge arrives on port 80. You get a timeout if a firewall or cloud security group is blocking it, and refused if the web server is down.
ss -tlnp | grep :80 # 누가 80을 점유 중인가
ufw status # 방화벽 상태If the web server is already using port 80 correctly, use webroot mode. If port 80 is free or standalone is simpler, stop the web server briefly and renew.
# standalone 모드: certbot이 직접 80을 임시 사용
systemctl stop nginx
certbot certonly --standalone -d example.com -d www.example.com
systemctl start nginx② webroot path error (Failed authorization procedure ... 404)
If the -w path does not match nginx/apache's actual document root, the challenge file is not found and you get a 404. Check nginx root or apache DocumentRoot.
grep -R "root" /etc/nginx/sites-enabled/
certbot certonly --webroot -w /var/www/html -d example.com③ DNS-01 validation failure
This happens with a wildcard (*.example.com) or a DNS challenge when the TXT record has not propagated or the A record is wrong.
dig +short example.com
dig +short TXT _acme-challenge.example.comFix the records, wait for propagation (TTL), then retry.
④ rate limit (too many certificates already issued for)
Let's Encrypt allows 5 duplicate certificates per week for the same registered domain set. Repeating --force-renewal during recovery easily hits it. If you are rate-limited, wait a week or finish validation against staging first.
# staging은 한도가 매우 넉넉하고, dry-run은 한도를 소모하지 않음
certbot renew --dry-run
certbot certonly --staging --standalone -d example.comKey point: --dry-run does not consume the rate limit. Always confirm a dry-run pass before a production issuance.
⑤ Already expired certificate (NET::ERR_CERT_DATE_INVALID)
Expiry itself is the result of renewal being blocked by one of the causes above. Remove the cause, then force-renew.
certbot renew --force-renewal
systemctl reload nginxPractical tip: During an overnight outage I once blindly ran
--force-renewalfirst, hit the rate limit, and burned more time detouring through staging. The order is always dry-run → fix the cause → actual renewal.
nginx vs apache: what's different
| Item | nginx | apache |
|---|---|---|
| Auto-config plugin | --nginx | --apache |
| reload hook | systemctl reload nginx | systemctl reload apache2 (or httpd) |
| Default webroot location | server { root ... } | DocumentRoot |
Using the plugin (--nginx/--apache) handles issuance, config, and reload in one shot. If you only obtained the certificate via webroot/standalone, you must reload yourself for it to take effect.
Permanently restoring auto-renewal: why cron died quietly
The usual reasons cron certbot auto-renewal stops working are three: ⓐ missing deploy-hook—renewal succeeded but reload never ran; ⓑ PATH issues in the cron environment so certbot isn't found; ⓒ snap vs apt path collision (/snap/bin/certbot vs /usr/bin/certbot).
The recommended install today is snap, which registers a systemd timer automatically. Check timer status first.
systemctl status certbot.timer
systemctl list-timers | grep certbotIf the timer is dead, enable it.
systemctl enable --now certbot.timerIf you use cron instead of a systemd timer, specify the absolute path and a deploy-hook.
# crontab -e
0 3 * * * /usr/bin/certbot renew --quiet --deploy-hook "systemctl reload nginx"--deploy-hook runs only when an actual renewal happened, so you avoid unnecessary reloads. Always pre-validate after configuring.
certbot renew --dry-runAs of 2026, Let's Encrypt is pushing short-lived certificates (on the order of days), so the reliability of renewal automation matters more than ever. Alternatives like acme.sh exist, but certbot snap + systemd timer is enough for most production environments.
Conclusion: recovery checklist and preventing recurrence
- Check expiry with
certbot certificates - Identify the cause among the five using the symptom→cause table
- Renew with standalone/webroot mode; use
--force-renewalif needed - Restore automation with
certbot.timeror cron +--deploy-hook - Periodically run
certbot renew --dry-runas a pre-check
Finally, set up 14-days-before-expiry alerts via monitoring (an external SSL expiry check or dry-run result notifications) so users never again discover the outage for you overnight.
Frequently Asked Questions (FAQ)
Q. Does running certbot renew --dry-run often hit the rate limit?
A. No. dry-run uses the Let's Encrypt staging servers, so it does not consume the production issuance limit (5 duplicates per week). Feel free to run it as often as you like for checks.
Q. Renewal succeeded but the browser still shows the expired certificate.
A. The web server did not reload the new certificate. Run systemctl reload nginx (or apache2/httpd) and add --deploy-hook to your automation.
Q. Should I use standalone or webroot? A. If the web server must keep occupying port 80 and zero downtime matters, use webroot. If port 80 is free or a brief stop is acceptable, standalone is simpler.
Nodelog는 모든 콘텐츠의 내용과 출처를 공개 전에 검토합니다. 환경(OS·버전)에 따라 결과가 달라질 수 있는 기술 정보는 공식 문서와 함께 확인하며, 검토 기준과 정정 원칙은 편집 정책에서 안내합니다. 오류를 발견하시면 이메일로 제보해 주세요 — 확인 후 신속히 정정합니다.
Comments
Be the first to comment.