[{"data":1,"prerenderedAt":1419},["ShallowReactive",2],{"\u002Fblog\u002Fssl-expiration-alerts":3},{"id":4,"title":5,"author":6,"body":8,"category":1409,"date":1410,"description":1411,"extension":1412,"image":1413,"lastUpdated":1413,"meta":1414,"navigation":484,"path":1415,"readingTime":583,"seo":1416,"stem":1417,"__hash__":1418},"blog\u002Fblog\u002Fssl-expiration-alerts.md","SSL Certificate Expiration Alerts: How to Set Them Up and What Thresholds to Use",{"name":7},"Vantaj Team",{"type":9,"value":10,"toc":1392},"minimark",[11,15,18,21,24,29,32,35,57,60,68,71,73,77,80,181,184,187,189,193,198,201,218,221,235,238,240,244,247,316,319,327,333,453,456,458,462,465,944,947,953,956,958,962,968,1034,1037,1043,1088,1091,1097,1100,1139,1142,1164,1167,1196,1202,1204,1208,1211,1215,1218,1252,1255,1259,1266,1283,1286,1290,1293,1296,1298,1302,1351,1355,1388],[12,13,14],"p",{},"An SSL certificate expiration alert fires when your certificate is getting close to its expiry date. That gives you days or weeks to renew it before browsers start blocking your site with security warnings.",[12,16,17],{},"The catch: most teams either do not have SSL expiration alerts configured, or they have them configured too late to be useful. An alert at 7 days gives you a week to debug a broken auto-renewal system. An alert at 1 day gives you a fire drill.",[12,19,20],{},"This guide covers what thresholds to use, how to set up alerts in common monitoring tools, what to do when an alert fires, and how to handle SSL monitoring across multiple domains.",[22,23],"hr",{},[25,26,28],"h2",{"id":27},"why-auto-renewal-is-not-enough","Why Auto-Renewal Is Not Enough",[12,30,31],{},"Let's Encrypt and ACME-based renewal tools solved the certificate renewal problem in principle. In practice, they silently fail more often than teams expect.",[12,33,34],{},"Auto-renewal fails when:",[36,37,38,42,45,48,51,54],"ul",{},[39,40,41],"li",{},"The ACME client (Certbot, acme.sh, etc.) stops running after a server restart or OS update",[39,43,44],{},"DNS validation records change after a migration or DNS provider switch",[39,46,47],{},"The web server configuration blocks HTTP-01 challenge responses",[39,49,50],{},"The renewal cron job runs but the write to disk fails due to permissions",[39,52,53],{},"The domain's CAA records are misconfigured and the CA cannot issue",[39,55,56],{},"A paid certificate's payment method expires and the CA cancels renewal",[12,58,59],{},"None of these failures produce visible errors. The renewal job runs, encounters a problem, and silently moves on. The certificate continues to serve fine - until it expires, at which point browsers start showing security warnings and your users cannot reach your site.",[12,61,62,63,67],{},"According to Keyfactor's 2025 State of Machine Identity report, ",[64,65,66],"strong",{},"67% of organizations experienced at least one certificate-related outage in the prior 24 months",". Companies including Microsoft Teams, Google Voice, and Slack have all had public outages from expired certificates. The root cause is almost always the same: a broken renewal process that nobody verified.",[12,69,70],{},"SSL expiration alerts exist specifically to catch this gap.",[22,72],{},[25,74,76],{"id":75},"the-right-alert-thresholds","The Right Alert Thresholds",[12,78,79],{},"Use tiered alerts. A single alert at 30 days means you see the problem once and then forget about it if you do not act immediately. Tiered alerts create escalating urgency.",[81,82,83,99],"table",{},[84,85,86],"thead",{},[87,88,89,93,96],"tr",{},[90,91,92],"th",{},"Alert threshold",[90,94,95],{},"Purpose",[90,97,98],{},"Action",[100,101,102,116,129,142,155,168],"tbody",{},[87,103,104,110,113],{},[105,106,107],"td",{},[64,108,109],{},"60 days",[105,111,112],{},"Early warning",[105,114,115],{},"Verify auto-renewal is configured and will succeed",[87,117,118,123,126],{},[105,119,120],{},[64,121,122],{},"30 days",[105,124,125],{},"Renewal action window",[105,127,128],{},"Test the renewal process manually. Create a ticket.",[87,130,131,136,139],{},[105,132,133],{},[64,134,135],{},"14 days",[105,137,138],{},"Escalation point",[105,140,141],{},"If renewal has not completed, escalate to a senior engineer",[87,143,144,149,152],{},[105,145,146],{},[64,147,148],{},"7 days",[105,150,151],{},"Fire drill threshold",[105,153,154],{},"Manually trigger renewal or request emergency cert from CA",[87,156,157,162,165],{},[105,158,159],{},[64,160,161],{},"3 days",[105,163,164],{},"Critical",[105,166,167],{},"All hands. Renew manually if auto-renewal has not completed.",[87,169,170,175,178],{},[105,171,172],{},[64,173,174],{},"1 day",[105,176,177],{},"Emergency",[105,179,180],{},"Renew now. Page whoever can execute this.",[12,182,183],{},"The 30-day alert is the most valuable. It gives you enough time to debug a broken renewal process without pressure. When a 30-day alert fires and you investigate, you find the problem with time to fix it properly. When a 7-day alert fires and you investigate, you fix it under pressure.",[12,185,186],{},"Most monitoring tools support two or three alert thresholds. If yours only supports one, set it at 30 days.",[22,188],{},[25,190,192],{"id":191},"how-to-set-up-ssl-expiration-alerts","How to Set Up SSL Expiration Alerts",[194,195,197],"h3",{"id":196},"in-vantaj","In Vantaj",[12,199,200],{},"Vantaj monitors SSL certificates automatically for every HTTPS monitor you create. There is no separate setup step.",[202,203,204,212,215],"ol",{},[39,205,206,207,211],{},"Add an HTTPS monitor for your domain (e.g., ",[208,209,210],"code",{},"https:\u002F\u002Fyourdomain.com",")",[39,213,214],{},"Vantaj detects the SSL certificate attached to the domain",[39,216,217],{},"Alerts fire automatically at 90, 60, 30, 14, 7, and 1 day before expiry",[12,219,220],{},"To adjust thresholds or change where alerts go:",[202,222,223,226,232],{},[39,224,225],{},"Open the monitor in your dashboard",[39,227,228,229],{},"Click ",[64,230,231],{},"SSL Certificate Monitoring",[39,233,234],{},"Edit the alert thresholds and notification channels",[12,236,237],{},"Free tier includes SSL expiration alerts. All paid plans include more notification channels (Slack, SMS, webhooks).",[22,239],{},[194,241,243],{"id":242},"using-openssl-for-manual-checks-no-tool-required","Using openssl for manual checks (no tool required)",[12,245,246],{},"Check any certificate's expiry date from the command line:",[248,249,254],"pre",{"className":250,"code":251,"language":252,"meta":253,"style":253},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","echo | openssl s_client -servername yourdomain.com -connect yourdomain.com:443 2>\u002Fdev\u002Fnull \\\n  | openssl x509 -noout -dates\n","bash","",[208,255,256,299],{"__ignoreMap":253},[257,258,261,265,269,273,277,280,283,286,289,292,295],"span",{"class":259,"line":260},"line",1,[257,262,264],{"class":263},"s2Zo4","echo",[257,266,268],{"class":267},"sMK4o"," |",[257,270,272],{"class":271},"sBMFI"," openssl",[257,274,276],{"class":275},"sfazB"," s_client",[257,278,279],{"class":275}," -servername",[257,281,282],{"class":275}," yourdomain.com",[257,284,285],{"class":275}," -connect",[257,287,288],{"class":275}," yourdomain.com:443",[257,290,291],{"class":267}," 2>",[257,293,294],{"class":275},"\u002Fdev\u002Fnull",[257,296,298],{"class":297},"sTEyZ"," \\\n",[257,300,302,305,307,310,313],{"class":259,"line":301},2,[257,303,304],{"class":267},"  |",[257,306,272],{"class":271},[257,308,309],{"class":275}," x509",[257,311,312],{"class":275}," -noout",[257,314,315],{"class":275}," -dates\n",[12,317,318],{},"Output:",[248,320,325],{"className":321,"code":323,"language":324},[322],"language-text","notBefore=Mar 15 00:00:00 2026 GMT\nnotAfter=Jun 14 23:59:59 2026 GMT\n","text",[208,326,323],{"__ignoreMap":253},[12,328,329,332],{},[208,330,331],{},"notAfter"," is the expiry date. Calculate days remaining:",[248,334,336],{"className":250,"code":335,"language":252,"meta":253,"style":253},"# Days until expiry\necho | openssl s_client -servername yourdomain.com -connect yourdomain.com:443 2>\u002Fdev\u002Fnull \\\n  | openssl x509 -noout -enddate \\\n  | sed 's\u002FnotAfter=\u002F\u002F' \\\n  | xargs -I {} date -d {} +%s \\\n  | xargs -I {} echo $(( ({} - $(date +%s)) \u002F 86400 )) days remaining\n",[208,337,338,344,368,384,403,430],{"__ignoreMap":253},[257,339,340],{"class":259,"line":260},[257,341,343],{"class":342},"sHwdD","# Days until expiry\n",[257,345,346,348,350,352,354,356,358,360,362,364,366],{"class":259,"line":301},[257,347,264],{"class":263},[257,349,268],{"class":267},[257,351,272],{"class":271},[257,353,276],{"class":275},[257,355,279],{"class":275},[257,357,282],{"class":275},[257,359,285],{"class":275},[257,361,288],{"class":275},[257,363,291],{"class":267},[257,365,294],{"class":275},[257,367,298],{"class":297},[257,369,371,373,375,377,379,382],{"class":259,"line":370},3,[257,372,304],{"class":267},[257,374,272],{"class":271},[257,376,309],{"class":275},[257,378,312],{"class":275},[257,380,381],{"class":275}," -enddate",[257,383,298],{"class":297},[257,385,387,389,392,395,398,401],{"class":259,"line":386},4,[257,388,304],{"class":267},[257,390,391],{"class":271}," sed",[257,393,394],{"class":267}," '",[257,396,397],{"class":275},"s\u002FnotAfter=\u002F\u002F",[257,399,400],{"class":267},"'",[257,402,298],{"class":297},[257,404,406,408,411,414,417,420,423,425,428],{"class":259,"line":405},5,[257,407,304],{"class":267},[257,409,410],{"class":271}," xargs",[257,412,413],{"class":275}," -I",[257,415,416],{"class":275}," {}",[257,418,419],{"class":275}," date",[257,421,422],{"class":275}," -d",[257,424,416],{"class":275},[257,426,427],{"class":275}," +%s",[257,429,298],{"class":297},[257,431,433,435,437,439,441,444,447,450],{"class":259,"line":432},6,[257,434,304],{"class":267},[257,436,410],{"class":271},[257,438,413],{"class":275},[257,440,416],{"class":275},[257,442,443],{"class":275}," echo",[257,445,446],{"class":267}," $((",[257,448,449],{"class":267}," (",[257,451,452],{"class":297},"{} - $(date +%s)) \u002F 86400 )) days remaining\n",[12,454,455],{},"This works for ad-hoc checks but does not scale to monitoring dozens of domains on a schedule.",[22,457],{},[194,459,461],{"id":460},"using-a-cron-job-for-self-managed-alerts","Using a cron job for self-managed alerts",[12,463,464],{},"For teams self-hosting monitoring, a cron job can check certificates and send email alerts:",[248,466,468],{"className":250,"code":467,"language":252,"meta":253,"style":253},"#!\u002Fbin\u002Fbash\n# ssl-check.sh - Run daily via cron\n\nDOMAINS=(\"yourdomain.com\" \"api.yourdomain.com\" \"app.yourdomain.com\")\nALERT_THRESHOLD=30  # days\nALERT_EMAIL=\"oncall@yourcompany.com\"\n\nfor DOMAIN in \"${DOMAINS[@]}\"; do\n  EXPIRY=$(echo | openssl s_client -servername \"$DOMAIN\" -connect \"$DOMAIN:443\" 2>\u002Fdev\u002Fnull \\\n    | openssl x509 -noout -enddate 2>\u002Fdev\u002Fnull \\\n    | sed 's\u002FnotAfter=\u002F\u002F')\n\n  if [ -z \"$EXPIRY\" ]; then\n    echo \"Could not fetch certificate for $DOMAIN\" \\\n      | mail -s \"SSL Check Failed: $DOMAIN\" \"$ALERT_EMAIL\"\n    continue\n  fi\n\n  EXPIRY_EPOCH=$(date -d \"$EXPIRY\" +%s 2>\u002Fdev\u002Fnull || date -j -f \"%b %d %T %Y %Z\" \"$EXPIRY\" +%s)\n  DAYS_LEFT=$(( (EXPIRY_EPOCH - $(date +%s)) \u002F 86400 ))\n\n  if [ \"$DAYS_LEFT\" -le \"$ALERT_THRESHOLD\" ]; then\n    echo \"SSL certificate for $DOMAIN expires in $DAYS_LEFT days ($EXPIRY)\" \\\n      | mail -s \"SSL Expiry Warning: $DOMAIN ($DAYS_LEFT days)\" \"$ALERT_EMAIL\"\n  fi\ndone\n",[208,469,470,475,480,486,520,534,549,554,581,624,644,659,664,689,706,734,740,746,751,804,841,846,874,902,933,938],{"__ignoreMap":253},[257,471,472],{"class":259,"line":260},[257,473,474],{"class":342},"#!\u002Fbin\u002Fbash\n",[257,476,477],{"class":259,"line":301},[257,478,479],{"class":342},"# ssl-check.sh - Run daily via cron\n",[257,481,482],{"class":259,"line":370},[257,483,485],{"emptyLinePlaceholder":484},true,"\n",[257,487,488,491,494,497,500,502,505,508,510,512,515,517],{"class":259,"line":386},[257,489,490],{"class":297},"DOMAINS",[257,492,493],{"class":267},"=(",[257,495,496],{"class":267},"\"",[257,498,499],{"class":275},"yourdomain.com",[257,501,496],{"class":267},[257,503,504],{"class":267}," \"",[257,506,507],{"class":275},"api.yourdomain.com",[257,509,496],{"class":267},[257,511,504],{"class":267},[257,513,514],{"class":275},"app.yourdomain.com",[257,516,496],{"class":267},[257,518,519],{"class":267},")\n",[257,521,522,525,528,531],{"class":259,"line":405},[257,523,524],{"class":297},"ALERT_THRESHOLD",[257,526,527],{"class":267},"=",[257,529,530],{"class":275},"30",[257,532,533],{"class":342},"  # days\n",[257,535,536,539,541,543,546],{"class":259,"line":432},[257,537,538],{"class":297},"ALERT_EMAIL",[257,540,527],{"class":267},[257,542,496],{"class":267},[257,544,545],{"class":275},"oncall@yourcompany.com",[257,547,548],{"class":267},"\"\n",[257,550,552],{"class":259,"line":551},7,[257,553,485],{"emptyLinePlaceholder":484},[257,555,557,561,564,567,570,572,575,578],{"class":259,"line":556},8,[257,558,560],{"class":559},"s7zQu","for",[257,562,563],{"class":297}," DOMAIN ",[257,565,566],{"class":559},"in",[257,568,569],{"class":267}," \"${",[257,571,490],{"class":297},[257,573,574],{"class":267},"[@]}\"",[257,576,577],{"class":267},";",[257,579,580],{"class":559}," do\n",[257,582,584,587,590,592,594,596,598,600,602,605,607,609,611,613,616,618,620,622],{"class":259,"line":583},9,[257,585,586],{"class":297},"  EXPIRY",[257,588,589],{"class":267},"=$(",[257,591,264],{"class":263},[257,593,268],{"class":267},[257,595,272],{"class":271},[257,597,276],{"class":275},[257,599,279],{"class":275},[257,601,504],{"class":267},[257,603,604],{"class":297},"$DOMAIN",[257,606,496],{"class":267},[257,608,285],{"class":275},[257,610,504],{"class":267},[257,612,604],{"class":297},[257,614,615],{"class":275},":443",[257,617,496],{"class":267},[257,619,291],{"class":267},[257,621,294],{"class":275},[257,623,298],{"class":297},[257,625,627,630,632,634,636,638,640,642],{"class":259,"line":626},10,[257,628,629],{"class":267},"    |",[257,631,272],{"class":271},[257,633,309],{"class":275},[257,635,312],{"class":275},[257,637,381],{"class":275},[257,639,291],{"class":267},[257,641,294],{"class":275},[257,643,298],{"class":297},[257,645,647,649,651,653,655,657],{"class":259,"line":646},11,[257,648,629],{"class":267},[257,650,391],{"class":271},[257,652,394],{"class":267},[257,654,397],{"class":275},[257,656,400],{"class":267},[257,658,519],{"class":267},[257,660,662],{"class":259,"line":661},12,[257,663,485],{"emptyLinePlaceholder":484},[257,665,667,670,673,676,678,681,683,686],{"class":259,"line":666},13,[257,668,669],{"class":559},"  if",[257,671,672],{"class":267}," [",[257,674,675],{"class":267}," -z",[257,677,504],{"class":267},[257,679,680],{"class":297},"$EXPIRY",[257,682,496],{"class":267},[257,684,685],{"class":267}," ];",[257,687,688],{"class":559}," then\n",[257,690,692,695,697,700,702,704],{"class":259,"line":691},14,[257,693,694],{"class":263},"    echo",[257,696,504],{"class":267},[257,698,699],{"class":275},"Could not fetch certificate for ",[257,701,604],{"class":297},[257,703,496],{"class":267},[257,705,298],{"class":297},[257,707,709,712,715,718,720,723,725,727,729,732],{"class":259,"line":708},15,[257,710,711],{"class":267},"      |",[257,713,714],{"class":271}," mail",[257,716,717],{"class":275}," -s",[257,719,504],{"class":267},[257,721,722],{"class":275},"SSL Check Failed: ",[257,724,604],{"class":297},[257,726,496],{"class":267},[257,728,504],{"class":267},[257,730,731],{"class":297},"$ALERT_EMAIL",[257,733,548],{"class":267},[257,735,737],{"class":259,"line":736},16,[257,738,739],{"class":559},"    continue\n",[257,741,743],{"class":259,"line":742},17,[257,744,745],{"class":559},"  fi\n",[257,747,749],{"class":259,"line":748},18,[257,750,485],{"emptyLinePlaceholder":484},[257,752,754,757,759,762,764,766,768,770,772,774,776,779,781,784,787,789,792,794,796,798,800,802],{"class":259,"line":753},19,[257,755,756],{"class":297},"  EXPIRY_EPOCH",[257,758,589],{"class":267},[257,760,761],{"class":271},"date",[257,763,422],{"class":275},[257,765,504],{"class":267},[257,767,680],{"class":297},[257,769,496],{"class":267},[257,771,427],{"class":275},[257,773,291],{"class":267},[257,775,294],{"class":275},[257,777,778],{"class":267}," ||",[257,780,419],{"class":271},[257,782,783],{"class":275}," -j",[257,785,786],{"class":275}," -f",[257,788,504],{"class":267},[257,790,791],{"class":275},"%b %d %T %Y %Z",[257,793,496],{"class":267},[257,795,504],{"class":267},[257,797,680],{"class":297},[257,799,496],{"class":267},[257,801,427],{"class":275},[257,803,519],{"class":267},[257,805,807,810,813,815,818,821,824,826,828,831,834,838],{"class":259,"line":806},20,[257,808,809],{"class":297},"  DAYS_LEFT",[257,811,812],{"class":267},"=$((",[257,814,449],{"class":267},[257,816,817],{"class":271},"EXPIRY_EPOCH",[257,819,820],{"class":275}," -",[257,822,823],{"class":267}," $(",[257,825,761],{"class":271},[257,827,427],{"class":275},[257,829,830],{"class":267},"))",[257,832,833],{"class":271}," \u002F",[257,835,837],{"class":836},"sbssI"," 86400",[257,839,840],{"class":267}," ))\n",[257,842,844],{"class":259,"line":843},21,[257,845,485],{"emptyLinePlaceholder":484},[257,847,849,851,853,855,858,860,863,865,868,870,872],{"class":259,"line":848},22,[257,850,669],{"class":559},[257,852,672],{"class":267},[257,854,504],{"class":267},[257,856,857],{"class":297},"$DAYS_LEFT",[257,859,496],{"class":267},[257,861,862],{"class":267}," -le",[257,864,504],{"class":267},[257,866,867],{"class":297},"$ALERT_THRESHOLD",[257,869,496],{"class":267},[257,871,685],{"class":267},[257,873,688],{"class":559},[257,875,877,879,881,884,886,889,891,894,896,898,900],{"class":259,"line":876},23,[257,878,694],{"class":263},[257,880,504],{"class":267},[257,882,883],{"class":275},"SSL certificate for ",[257,885,604],{"class":297},[257,887,888],{"class":275}," expires in ",[257,890,857],{"class":297},[257,892,893],{"class":275}," days (",[257,895,680],{"class":297},[257,897,211],{"class":275},[257,899,496],{"class":267},[257,901,298],{"class":297},[257,903,905,907,909,911,913,916,918,920,922,925,927,929,931],{"class":259,"line":904},24,[257,906,711],{"class":267},[257,908,714],{"class":271},[257,910,717],{"class":275},[257,912,504],{"class":267},[257,914,915],{"class":275},"SSL Expiry Warning: ",[257,917,604],{"class":297},[257,919,449],{"class":275},[257,921,857],{"class":297},[257,923,924],{"class":275}," days)",[257,926,496],{"class":267},[257,928,504],{"class":267},[257,930,731],{"class":297},[257,932,548],{"class":267},[257,934,936],{"class":259,"line":935},25,[257,937,745],{"class":559},[257,939,941],{"class":259,"line":940},26,[257,942,943],{"class":559},"done\n",[12,945,946],{},"Add to crontab to run daily at 8 AM:",[248,948,951],{"className":949,"code":950,"language":324},[322],"0 8 * * * \u002Fpath\u002Fto\u002Fssl-check.sh\n",[208,952,950],{"__ignoreMap":253},[12,954,955],{},"This covers basic expiry checking but misses chain validation and hostname verification that dedicated monitoring tools include.",[22,957],{},[25,959,961],{"id":960},"what-to-do-when-an-ssl-alert-fires","What to Do When an SSL Alert Fires",[12,963,964,967],{},[64,965,966],{},"At 60 days:"," No urgency. Verify the auto-renewal configuration is intact.",[248,969,971],{"className":250,"code":970,"language":252,"meta":253,"style":253},"# Check Certbot timer status\nsystemctl status certbot.timer\n\n# Check last renewal log\ncat \u002Fvar\u002Flog\u002Fletsencrypt\u002Fletsencrypt.log | tail -50\n\n# Test renewal in dry-run mode (does not actually renew)\ncertbot renew --dry-run\n",[208,972,973,978,989,993,998,1014,1018,1023],{"__ignoreMap":253},[257,974,975],{"class":259,"line":260},[257,976,977],{"class":342},"# Check Certbot timer status\n",[257,979,980,983,986],{"class":259,"line":301},[257,981,982],{"class":271},"systemctl",[257,984,985],{"class":275}," status",[257,987,988],{"class":275}," certbot.timer\n",[257,990,991],{"class":259,"line":370},[257,992,485],{"emptyLinePlaceholder":484},[257,994,995],{"class":259,"line":386},[257,996,997],{"class":342},"# Check last renewal log\n",[257,999,1000,1003,1006,1008,1011],{"class":259,"line":405},[257,1001,1002],{"class":271},"cat",[257,1004,1005],{"class":275}," \u002Fvar\u002Flog\u002Fletsencrypt\u002Fletsencrypt.log",[257,1007,268],{"class":267},[257,1009,1010],{"class":271}," tail",[257,1012,1013],{"class":275}," -50\n",[257,1015,1016],{"class":259,"line":432},[257,1017,485],{"emptyLinePlaceholder":484},[257,1019,1020],{"class":259,"line":551},[257,1021,1022],{"class":342},"# Test renewal in dry-run mode (does not actually renew)\n",[257,1024,1025,1028,1031],{"class":259,"line":556},[257,1026,1027],{"class":271},"certbot",[257,1029,1030],{"class":275}," renew",[257,1032,1033],{"class":275}," --dry-run\n",[12,1035,1036],{},"If the dry-run succeeds, auto-renewal will work when the time comes. Log this and move on.",[12,1038,1039,1042],{},[64,1040,1041],{},"At 30 days:"," Trigger a manual dry-run renewal to verify the process will succeed. If it fails, fix it now.",[248,1044,1046],{"className":250,"code":1045,"language":252,"meta":253,"style":253},"# Dry run for a specific domain\ncertbot renew --cert-name yourdomain.com --dry-run\n\n# Check if renewal succeeds with forced renewal (use carefully - counts against rate limits)\ncertbot renew --cert-name yourdomain.com --force-renewal\n",[208,1047,1048,1053,1066,1070,1075],{"__ignoreMap":253},[257,1049,1050],{"class":259,"line":260},[257,1051,1052],{"class":342},"# Dry run for a specific domain\n",[257,1054,1055,1057,1059,1062,1064],{"class":259,"line":301},[257,1056,1027],{"class":271},[257,1058,1030],{"class":275},[257,1060,1061],{"class":275}," --cert-name",[257,1063,282],{"class":275},[257,1065,1033],{"class":275},[257,1067,1068],{"class":259,"line":370},[257,1069,485],{"emptyLinePlaceholder":484},[257,1071,1072],{"class":259,"line":386},[257,1073,1074],{"class":342},"# Check if renewal succeeds with forced renewal (use carefully - counts against rate limits)\n",[257,1076,1077,1079,1081,1083,1085],{"class":259,"line":405},[257,1078,1027],{"class":271},[257,1080,1030],{"class":275},[257,1082,1061],{"class":275},[257,1084,282],{"class":275},[257,1086,1087],{"class":275}," --force-renewal\n",[12,1089,1090],{},"Create a ticket so the renewal does not fall through the cracks.",[12,1092,1093,1096],{},[64,1094,1095],{},"At 14 days:"," If the certificate has not renewed yet, escalate. Either the renewal happened and monitoring is not detecting the new certificate (check if the web server reloaded after renewal), or the renewal is still failing.",[12,1098,1099],{},"Check if the web server reloaded after the last renewal attempt:",[248,1101,1103],{"className":250,"code":1102,"language":252,"meta":253,"style":253},"# Check current certificate expiry on the live server\necho | openssl s_client -connect yourdomain.com:443 2>\u002Fdev\u002Fnull | openssl x509 -noout -enddate\n",[208,1104,1105,1110],{"__ignoreMap":253},[257,1106,1107],{"class":259,"line":260},[257,1108,1109],{"class":342},"# Check current certificate expiry on the live server\n",[257,1111,1112,1114,1116,1118,1120,1122,1124,1126,1128,1130,1132,1134,1136],{"class":259,"line":301},[257,1113,264],{"class":263},[257,1115,268],{"class":267},[257,1117,272],{"class":271},[257,1119,276],{"class":275},[257,1121,285],{"class":275},[257,1123,288],{"class":275},[257,1125,291],{"class":267},[257,1127,294],{"class":275},[257,1129,268],{"class":267},[257,1131,272],{"class":271},[257,1133,309],{"class":275},[257,1135,312],{"class":275},[257,1137,1138],{"class":275}," -enddate\n",[12,1140,1141],{},"Compare this against the certificate file on disk:",[248,1143,1145],{"className":250,"code":1144,"language":252,"meta":253,"style":253},"openssl x509 -noout -enddate -in \u002Fetc\u002Fletsencrypt\u002Flive\u002Fyourdomain.com\u002Fcert.pem\n",[208,1146,1147],{"__ignoreMap":253},[257,1148,1149,1152,1154,1156,1158,1161],{"class":259,"line":260},[257,1150,1151],{"class":271},"openssl",[257,1153,309],{"class":275},[257,1155,312],{"class":275},[257,1157,381],{"class":275},[257,1159,1160],{"class":275}," -in",[257,1162,1163],{"class":275}," \u002Fetc\u002Fletsencrypt\u002Flive\u002Fyourdomain.com\u002Fcert.pem\n",[12,1165,1166],{},"If the file is renewed but the server still serves the old certificate, the web server needs to reload:",[248,1168,1170],{"className":250,"code":1169,"language":252,"meta":253,"style":253},"systemctl reload nginx\n# or\nsystemctl reload apache2\n",[208,1171,1172,1182,1187],{"__ignoreMap":253},[257,1173,1174,1176,1179],{"class":259,"line":260},[257,1175,982],{"class":271},[257,1177,1178],{"class":275}," reload",[257,1180,1181],{"class":275}," nginx\n",[257,1183,1184],{"class":259,"line":301},[257,1185,1186],{"class":342},"# or\n",[257,1188,1189,1191,1193],{"class":259,"line":370},[257,1190,982],{"class":271},[257,1192,1178],{"class":275},[257,1194,1195],{"class":275}," apache2\n",[12,1197,1198,1201],{},[64,1199,1200],{},"At 7 days or fewer:"," Manual intervention required. Either force a renewal or request a new certificate directly.",[22,1203],{},[25,1205,1207],{"id":1206},"managing-ssl-alerts-across-multiple-domains","Managing SSL Alerts Across Multiple Domains",[12,1209,1210],{},"Single-domain monitoring is straightforward. Multiple domains require a systematic approach.",[194,1212,1214],{"id":1213},"build-an-inventory-first","Build an inventory first",[12,1216,1217],{},"List every domain and subdomain that serves HTTPS:",[36,1219,1220,1229,1234,1240,1246,1249],{},[39,1221,1222,1223,1225,1226,211],{},"Production domains (",[208,1224,499],{},", ",[208,1227,1228],{},"www.yourdomain.com",[39,1230,1231,1232,211],{},"API subdomains (",[208,1233,507],{},[39,1235,1236,1237,211],{},"CDN or asset subdomains (",[208,1238,1239],{},"assets.yourdomain.com",[39,1241,1242,1243,211],{},"Staging and preview environments (",[208,1244,1245],{},"staging.yourdomain.com",[39,1247,1248],{},"Internal tools served over HTTPS",[39,1250,1251],{},"Customer-facing subdomains (if you provide these)",[12,1253,1254],{},"Teams consistently undercount. A mid-size SaaS typically has 15-30 certificates in play. Add each one to your monitoring tool.",[194,1256,1258],{"id":1257},"watch-for-wildcard-certificate-gaps","Watch for wildcard certificate gaps",[12,1260,1261,1262,1265],{},"A wildcard certificate (",[208,1263,1264],{},"*.yourdomain.com",") covers one level of subdomain depth. It does not cover:",[36,1267,1268,1274],{},[39,1269,1270,1271,1273],{},"The apex domain (",[208,1272,499],{},") - needs a separate cert or multi-domain cert",[39,1275,1276,1277,1280,1281],{},"Second-level subdomains (",[208,1278,1279],{},"api.internal.yourdomain.com",") - not covered by ",[208,1282,1264],{},[12,1284,1285],{},"Monitor both the wildcard and apex separately.",[194,1287,1289],{"id":1288},"set-up-alerts-per-certificate-not-per-domain","Set up alerts per certificate, not per domain",[12,1291,1292],{},"On CDN-fronted domains, the certificate your monitoring tool sees may be the CDN's certificate, not yours. If you use Cloudflare in proxy mode, Cloudflare serves its own certificate to clients. Your origin certificate (between Cloudflare and your server) is a separate cert that also needs monitoring.",[12,1294,1295],{},"Monitor the origin certificate separately by checking it directly against your origin IP or by adding a check that bypasses the CDN.",[22,1297],{},[25,1299,1301],{"id":1300},"quick-setup-checklist","Quick Setup Checklist",[36,1303,1306,1315,1321,1327,1333,1339,1345],{"className":1304},[1305],"contains-task-list",[39,1307,1310,1314],{"className":1308},[1309],"task-list-item",[1311,1312],"input",{"disabled":484,"type":1313},"checkbox"," SSL alerts configured for every production HTTPS endpoint",[39,1316,1318,1320],{"className":1317},[1309],[1311,1319],{"disabled":484,"type":1313}," Alert thresholds set at 30 days and 7 days minimum",[39,1322,1324,1326],{"className":1323},[1309],[1311,1325],{"disabled":484,"type":1313}," Alerts route to your team's active channel (Slack, email, SMS)",[39,1328,1330,1332],{"className":1329},[1309],[1311,1331],{"disabled":484,"type":1313}," Staging and internal HTTPS endpoints also monitored",[39,1334,1336,1338],{"className":1335},[1309],[1311,1337],{"disabled":484,"type":1313}," Wildcard and apex certificates monitored separately",[39,1340,1342,1344],{"className":1341},[1309],[1311,1343],{"disabled":484,"type":1313}," Auto-renewal verified with a dry-run today",[39,1346,1348,1350],{"className":1347},[1309],[1311,1349],{"disabled":484,"type":1313}," Post-renewal check added: confirm web server serves the new certificate after renewal",[25,1352,1354],{"id":1353},"related-guides","Related Guides",[36,1356,1357,1364,1370,1376,1382],{},[39,1358,1359],{},[1360,1361,1363],"a",{"href":1362},"\u002Fblog\u002Fssl-certificate-monitoring","SSL Certificate Monitoring Guide",[39,1365,1366],{},[1360,1367,1369],{"href":1368},"\u002Fblog\u002Fssl-certificate-expiry-outages","The SSL Outage Nobody Saw Coming",[39,1371,1372],{},[1360,1373,1375],{"href":1374},"\u002Fblog\u002Fbest-ssl-monitoring-tools","Best SSL Certificate Monitoring Tools in 2026",[39,1377,1378],{},[1360,1379,1381],{"href":1380},"\u002Fblog\u002Fwhy-website-keeps-going-down","Why Your Website Keeps Going Down",[39,1383,1384],{},[1360,1385,1387],{"href":1386},"\u002Fblog\u002Fdns-monitoring-guide","DNS Monitoring Guide",[1389,1390,1391],"style",{},"html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":253,"searchDepth":301,"depth":301,"links":1393},[1394,1395,1396,1401,1402,1407,1408],{"id":27,"depth":301,"text":28},{"id":75,"depth":301,"text":76},{"id":191,"depth":301,"text":192,"children":1397},[1398,1399,1400],{"id":196,"depth":370,"text":197},{"id":242,"depth":370,"text":243},{"id":460,"depth":370,"text":461},{"id":960,"depth":301,"text":961},{"id":1206,"depth":301,"text":1207,"children":1403},[1404,1405,1406],{"id":1213,"depth":370,"text":1214},{"id":1257,"depth":370,"text":1258},{"id":1288,"depth":370,"text":1289},{"id":1300,"depth":301,"text":1301},{"id":1353,"depth":301,"text":1354},"tutorials","2026-07-03","SSL expiration alerts give you weeks to fix a renewal problem before it becomes an outage. This guide covers alert thresholds, setup steps, what to do when an alert fires, and how to manage SSL across multiple domains.","md",null,{},"\u002Fblog\u002Fssl-expiration-alerts",{"title":5,"description":1411},"blog\u002Fssl-expiration-alerts","amgK7icR9Ni1OtZb2ekfdJzDp_MaYWxjlT13bpB1BFs",1782668040171]