[{"data":1,"prerenderedAt":684},["ShallowReactive",2],{"blog-how-to/certificate-renewal":3},{"id":4,"title":5,"body":6,"category":658,"date":659,"dateModified":659,"description":660,"draft":661,"extension":662,"faq":663,"featured":661,"headerVariant":670,"image":671,"keywords":671,"meta":672,"navigation":673,"ogDescription":674,"ogTitle":671,"path":675,"readTime":671,"schemaOrg":676,"schemaType":677,"seo":678,"sitemap":679,"stem":680,"tags":681,"twitterCard":682,"__hash__":683},"blog/blog/how-to/certificate-renewal.md","How to Handle SSL Certificate Renewal",{"type":7,"value":8,"toc":616},"minimark",[9,13,17,21,35,40,56,60,145,149,168,181,191,195,207,219,243,256,260,275,293,326,330,335,367,371,383,395,407,416,420,424,430,434,440,444,448,454,458,464,468,474,478,484,488,494,498,504,508,514,529,584,592],[10,11],"category-badge",{"category":12},"How-To Guide",[14,15,5],"h1",{"id":16},"how-to-handle-ssl-certificate-renewal",[18,19,20],"p",{},"Never let your certificates expire unexpectedly",[22,23,24,27],"tldr",{},[18,25,26],{},"TL;DR (15 minutes):",[18,28,29,30,34],{},"For Let's Encrypt: Certbot auto-renews via systemd timer or cron - verify with ",[31,32,33],"code",{},"sudo certbot renew --dry-run",". For platform-hosted sites (Vercel, Netlify), renewal is automatic. Monitor expiration with free tools like SSL Labs, UptimeRobot, or a simple cron script. Most renewal failures are caused by blocked ports or stopped web servers.",[36,37,39],"h2",{"id":38},"prerequisites","Prerequisites",[41,42,43,47,50,53],"ul",{},[44,45,46],"li",{},"An existing SSL certificate installed on your server or platform",[44,48,49],{},"For manual servers: SSH access with sudo privileges",[44,51,52],{},"For Let's Encrypt: Certbot already installed",[44,54,55],{},"Email address for expiration alerts",[36,57,59],{"id":58},"certificate-renewal-by-platform","Certificate Renewal by Platform",[61,62,63,79],"table",{},[64,65,66],"thead",{},[67,68,69,73,76],"tr",{},[70,71,72],"th",{},"Platform",[70,74,75],{},"Renewal Method",[70,77,78],{},"Your Action Required",[80,81,82,94,103,112,123,134],"tbody",{},[67,83,84,88,91],{},[85,86,87],"td",{},"Vercel",[85,89,90],{},"Fully automatic",[85,92,93],{},"None - handled by platform",[67,95,96,99,101],{},[85,97,98],{},"Netlify",[85,100,90],{},[85,102,93],{},[67,104,105,108,110],{},[85,106,107],{},"Cloudflare",[85,109,90],{},[85,111,93],{},[67,113,114,117,120],{},[85,115,116],{},"AWS CloudFront + ACM",[85,118,119],{},"Automatic (ACM)",[85,121,122],{},"None - ACM auto-renews",[67,124,125,128,131],{},[85,126,127],{},"VPS with Certbot",[85,129,130],{},"Automatic (timer/cron)",[85,132,133],{},"Verify timer is running",[67,135,136,139,142],{},[85,137,138],{},"Commercial certificate",[85,140,141],{},"Manual",[85,143,144],{},"Renew before expiration",[36,146,148],{"id":147},"check-certificate-expiration-date","Check Certificate Expiration Date",[150,151,153,158],"step",{"number":152},"1",[154,155,157],"h3",{"id":156},"from-command-line","From command line",[159,160,165],"pre",{"className":161,"code":163,"language":164},[162],"language-text","# Check expiration for any domain\necho | openssl s_client -servername yourdomain.com -connect yourdomain.com:443 2>/dev/null | openssl x509 -noout -dates\n\n# Output shows:\n# notBefore=Jan  1 00:00:00 2024 GMT\n# notAfter=Apr  1 00:00:00 2024 GMT\n\n# More detailed certificate info:\necho | openssl s_client -servername yourdomain.com -connect yourdomain.com:443 2>/dev/null | openssl x509 -noout -subject -issuer -dates\n","text",[31,166,163],{"__ignoreMap":167},"",[150,169,171,175],{"number":170},"2",[154,172,174],{"id":173},"using-certbot","Using Certbot",[159,176,179],{"className":177,"code":178,"language":164},[162],"# List all certificates and their expiration\nsudo certbot certificates\n\n# Output shows:\n# Certificate Name: yourdomain.com\n#   Domains: yourdomain.com www.yourdomain.com\n#   Expiry Date: 2024-04-01 (VALID: 45 days)\n",[31,180,178],{"__ignoreMap":167},[150,182,184,188],{"number":183},"3",[154,185,187],{"id":186},"in-browser","In browser",[18,189,190],{},"Click the padlock icon > \"Connection is secure\" > \"Certificate is valid\" to view expiration date.",[36,192,194],{"id":193},"set-up-automatic-renewal-certbot","Set Up Automatic Renewal (Certbot)",[150,196,197,201],{"number":152},[154,198,200],{"id":199},"verify-auto-renewal-is-configured","Verify auto-renewal is configured",[159,202,205],{"className":203,"code":204,"language":164},[162],"# Check if systemd timer exists (most systems)\nsystemctl list-timers | grep certbot\n\n# Should show something like:\n# NEXT                        LEFT          UNIT                  ACTIVATES\n# Mon 2024-01-15 03:42:00 UTC 5h left       certbot.timer         certbot.service\n\n# Or check cron\ncat /etc/cron.d/certbot\n# Should show renewal command running twice daily\n",[31,206,204],{"__ignoreMap":167},[150,208,209,213],{"number":170},[154,210,212],{"id":211},"test-renewal-dry-run","Test renewal (dry run)",[159,214,217],{"className":215,"code":216,"language":164},[162],"# Always test before relying on auto-renewal\nsudo certbot renew --dry-run\n\n# Success output:\n# Congratulations, all simulated renewals succeeded:\n#   /etc/letsencrypt/live/yourdomain.com/fullchain.pem (success)\n\n# If this fails, fix the issue before your certificate expires\n",[31,218,216],{"__ignoreMap":167},[150,220,221,225,231,237],{"number":183},[154,222,224],{"id":223},"set-up-systemd-timer-if-missing","Set up systemd timer (if missing)",[159,226,229],{"className":227,"code":228,"language":164},[162],"# Create timer file\nsudo nano /etc/systemd/system/certbot.timer\n\n# Add content:\n[Unit]\nDescription=Run certbot twice daily\n\n[Timer]\nOnCalendar=*-*-* 00,12:00:00\nRandomizedDelaySec=43200\nPersistent=true\n\n[Install]\nWantedBy=timers.target\n",[31,230,228],{"__ignoreMap":167},[159,232,235],{"className":233,"code":234,"language":164},[162],"# Create service file\nsudo nano /etc/systemd/system/certbot.service\n\n# Add content:\n[Unit]\nDescription=Certbot renewal\n\n[Service]\nType=oneshot\nExecStart=/usr/bin/certbot renew --quiet --deploy-hook \"systemctl reload nginx\"\n",[31,236,234],{"__ignoreMap":167},[159,238,241],{"className":239,"code":240,"language":164},[162],"# Enable and start timer\nsudo systemctl enable certbot.timer\nsudo systemctl start certbot.timer\n\n# Verify it's running\nsystemctl status certbot.timer\n",[31,242,240],{"__ignoreMap":167},[150,244,246,250],{"number":245},"4",[154,247,249],{"id":248},"alternative-cron-job","Alternative: Cron job",[159,251,254],{"className":252,"code":253,"language":164},[162],"# Add to crontab\nsudo crontab -e\n\n# Add this line (runs at 3:30 AM and 3:30 PM)\n30 3,15 * * * /usr/bin/certbot renew --quiet --deploy-hook \"systemctl reload nginx\"\n\n# The --deploy-hook runs only if renewal occurred\n# Change nginx to apache2 if using Apache\n",[31,255,253],{"__ignoreMap":167},[36,257,259],{"id":258},"set-up-renewal-notifications","Set Up Renewal Notifications",[150,261,262,266,269],{"number":152},[154,263,265],{"id":264},"lets-encrypt-email-notifications","Let's Encrypt email notifications",[18,267,268],{},"Let's Encrypt emails you 20 days before expiration if not renewed:",[159,270,273],{"className":271,"code":272,"language":164},[162],"# Update email in Certbot\nsudo certbot update_account --email your@email.com\n\n# Verify email is set\nsudo certbot show_account\n",[31,274,272],{"__ignoreMap":167},[150,276,277,281,287],{"number":170},[154,278,280],{"id":279},"create-monitoring-script","Create monitoring script",[159,282,285],{"className":283,"code":284,"language":164},[162],"#!/bin/bash\n# save as /usr/local/bin/check-ssl-expiry.sh\n\nDOMAIN=\"yourdomain.com\"\nALERT_DAYS=14\nEMAIL=\"your@email.com\"\n\n# Get expiration date\nEXPIRY=$(echo | openssl s_client -servername $DOMAIN -connect $DOMAIN:443 2>/dev/null | openssl x509 -noout -enddate | cut -d= -f2)\n\n# Convert to epoch\nEXPIRY_EPOCH=$(date -d \"$EXPIRY\" +%s)\nNOW_EPOCH=$(date +%s)\nDAYS_LEFT=$(( ($EXPIRY_EPOCH - $NOW_EPOCH) / 86400 ))\n\nif [ $DAYS_LEFT -lt $ALERT_DAYS ]; then\n    echo \"SSL certificate for $DOMAIN expires in $DAYS_LEFT days!\" | \\\n    mail -s \"SSL Certificate Expiring: $DOMAIN\" $EMAIL\nfi\n",[31,286,284],{"__ignoreMap":167},[159,288,291],{"className":289,"code":290,"language":164},[162],"# Make executable and add to cron\nchmod +x /usr/local/bin/check-ssl-expiry.sh\nsudo crontab -e\n# Add: 0 9 * * * /usr/local/bin/check-ssl-expiry.sh\n",[31,292,290],{"__ignoreMap":167},[150,294,295,299],{"number":183},[154,296,298],{"id":297},"use-external-monitoring-services","Use external monitoring services",[41,300,301,308,314,320],{},[44,302,303,307],{},[304,305,306],"strong",{},"UptimeRobot:"," Free SSL monitoring with email/SMS alerts",[44,309,310,313],{},[304,311,312],{},"SSL Labs:"," Bookmark your test URL, check periodically",[44,315,316,319],{},[304,317,318],{},"Datadog/PagerDuty:"," Enterprise monitoring with SSL checks",[44,321,322,325],{},[304,323,324],{},"Oh Dear:"," Affordable monitoring with certificate checks",[36,327,329],{"id":328},"security-checklist","Security Checklist",[331,332,334],"h4",{"id":333},"certificate-renewal-security-checklist","Certificate Renewal Security Checklist",[41,336,337,340,343,346,349,352,355,358,361,364],{},[44,338,339],{},"Auto-renewal configured (systemd timer or cron)",[44,341,342],{},"Dry-run test passes successfully",[44,344,345],{},"Deploy hook reloads web server after renewal",[44,347,348],{},"Email notifications configured for Let's Encrypt",[44,350,351],{},"External monitoring set up for expiration alerts",[44,353,354],{},"Port 80 accessible for HTTP-01 challenge (or DNS challenge configured)",[44,356,357],{},"Logs monitored for renewal failures",[44,359,360],{},"Backup renewal process documented",[44,362,363],{},"Multiple domains in certificate are all valid",[44,365,366],{},"Calendar reminder set for annual review",[36,368,370],{"id":369},"how-to-verify-it-worked","How to Verify It Worked",[150,372,373,377],{"number":152},[154,374,376],{"id":375},"check-certificate-after-renewal","Check certificate after renewal",[159,378,381],{"className":379,"code":380,"language":164},[162],"# Verify new expiration date\necho | openssl s_client -servername yourdomain.com -connect yourdomain.com:443 2>/dev/null | openssl x509 -noout -dates\n\n# Should show dates ~90 days in the future for Let's Encrypt\n",[31,382,380],{"__ignoreMap":167},[150,384,385,389],{"number":170},[154,386,388],{"id":387},"check-certbot-logs","Check Certbot logs",[159,390,393],{"className":391,"code":392,"language":164},[162],"# View recent renewal attempts\nsudo tail -100 /var/log/letsencrypt/letsencrypt.log\n\n# Look for:\n# \"Cert is due for renewal, auto-renewing...\"\n# \"Congratulations, all renewals succeeded\"\n",[31,394,392],{"__ignoreMap":167},[150,396,397,401],{"number":183},[154,398,400],{"id":399},"verify-web-server-reloaded","Verify web server reloaded",[159,402,405],{"className":403,"code":404,"language":164},[162],"# Check nginx/apache is serving new certificate\n# The serial number should change after renewal\necho | openssl s_client -servername yourdomain.com -connect yourdomain.com:443 2>/dev/null | openssl x509 -noout -serial\n",[31,406,404],{"__ignoreMap":167},[150,408,409,413],{"number":245},[154,410,412],{"id":411},"test-site-functionality","Test site functionality",[18,414,415],{},"After renewal, verify your site loads correctly over HTTPS with no certificate warnings.",[36,417,419],{"id":418},"manual-renewal-when-needed","Manual Renewal (When Needed)",[154,421,423],{"id":422},"force-immediate-renewal","Force immediate renewal",[159,425,428],{"className":426,"code":427,"language":164},[162],"# Force renewal even if not due\nsudo certbot renew --force-renewal\n\n# Renew specific certificate\nsudo certbot certonly --force-renewal -d yourdomain.com -d www.yourdomain.com\n\n# Reload web server after manual renewal\nsudo systemctl reload nginx  # or apache2\n",[31,429,427],{"__ignoreMap":167},[154,431,433],{"id":432},"renew-with-standalone-server","Renew with standalone server",[159,435,438],{"className":436,"code":437,"language":164},[162],"# If web server is causing issues, use standalone mode\nsudo systemctl stop nginx\nsudo certbot renew --standalone\nsudo systemctl start nginx\n",[31,439,437],{"__ignoreMap":167},[36,441,443],{"id":442},"common-errors-and-troubleshooting","Common Errors and Troubleshooting",[154,445,447],{"id":446},"challenge-failed-connection-refused-on-port-80","Challenge failed: Connection refused on port 80",[159,449,452],{"className":450,"code":451,"language":164},[162],"# Firewall blocking port 80\nsudo ufw allow 80/tcp\nsudo ufw status\n\n# Or check iptables\nsudo iptables -L -n | grep 80\n\n# Some hosts require port 80 open even for HTTPS sites\n# Let's Encrypt needs it for HTTP-01 challenge\n",[31,453,451],{"__ignoreMap":167},[154,455,457],{"id":456},"could-not-bind-to-port-80","Could not bind to port 80",[159,459,462],{"className":460,"code":461,"language":164},[162],"# Another service is using port 80\nsudo lsof -i :80\n\n# If it's your web server, use webroot or nginx plugin instead of standalone\nsudo certbot renew --webroot -w /var/www/html\n\n# Or use the nginx/apache plugin\nsudo certbot renew --nginx\n",[31,463,461],{"__ignoreMap":167},[154,465,467],{"id":466},"dns-problem-nxdomain-for-domain","DNS problem: NXDOMAIN for domain",[159,469,472],{"className":470,"code":471,"language":164},[162],"# Domain DNS not resolving\ndig yourdomain.com A\n\n# Verify DNS points to your server's IP\n# If DNS changed recently, wait for propagation\n\n# For DNS-01 challenge (wildcard certs), ensure TXT records are correct\n",[31,473,471],{"__ignoreMap":167},[154,475,477],{"id":476},"too-many-certificates-already-issued","Too many certificates already issued",[159,479,482],{"className":480,"code":481,"language":164},[162],"# Let's Encrypt rate limit: 50 certs per domain per week\n# Wait a week, or use staging for testing:\nsudo certbot certonly --staging -d yourdomain.com\n\n# Use --expand to add domains to existing certificate instead of new cert\nsudo certbot certonly --expand -d yourdomain.com -d newsubdomain.yourdomain.com\n",[31,483,481],{"__ignoreMap":167},[154,485,487],{"id":486},"web-server-not-reloading-after-renewal","Web server not reloading after renewal",[159,489,492],{"className":490,"code":491,"language":164},[162],"# Add deploy hook to reload automatically\nsudo certbot renew --deploy-hook \"systemctl reload nginx\"\n\n# Or edit /etc/letsencrypt/renewal/yourdomain.com.conf:\n# Under [renewalparams], add:\n# renew_hook = systemctl reload nginx\n\n# Verify hook is set:\ncat /etc/letsencrypt/renewal/yourdomain.com.conf | grep hook\n",[31,493,491],{"__ignoreMap":167},[154,495,497],{"id":496},"certificate-renewed-but-site-shows-old-certificate","Certificate renewed but site shows old certificate",[159,499,502],{"className":500,"code":501,"language":164},[162],"# Web server needs to reload\nsudo systemctl reload nginx  # or apache2\n\n# Check nginx is using correct certificate path\ngrep ssl_certificate /etc/nginx/sites-enabled/*\n\n# Should point to /etc/letsencrypt/live/yourdomain.com/fullchain.pem\n\n# If using a reverse proxy or CDN, their cache may need clearing\n",[31,503,501],{"__ignoreMap":167},[154,505,507],{"id":506},"renewal-works-manually-but-not-automatically","Renewal works manually but not automatically",[159,509,512],{"className":510,"code":511,"language":164},[162],"# Check systemd timer is running\nsystemctl status certbot.timer\n\n# Check for path issues in cron\n# Use full paths in cron jobs:\n/usr/bin/certbot renew --quiet\n\n# Check cron logs\ngrep certbot /var/log/syslog\n\n# Ensure certbot user has necessary permissions\n",[31,513,511],{"__ignoreMap":167},[515,516,517,520,523],"warning-box",{},[18,518,519],{},"Emergency: Certificate Already Expired",[18,521,522],{},"If your certificate has already expired, users see security warnings. Act immediately:",[159,524,527],{"className":525,"code":526,"language":164},[162],"# Try immediate renewal\nsudo certbot renew --force-renewal\n\n# If that fails, get new certificate\nsudo certbot certonly --nginx -d yourdomain.com -d www.yourdomain.com\n\n# Reload web server\nsudo systemctl reload nginx\n",[31,528,526],{"__ignoreMap":167},[530,531,532,539,545,555,565,571],"faq-section",{},[533,534,536],"faq-item",{"question":535},"How often do SSL certificates need to be renewed?",[18,537,538],{},"Let's Encrypt certificates expire every 90 days, but Certbot attempts renewal when 30 days remain. Commercial certificates typically last 1-2 years. Platform-managed certificates (Vercel, Netlify, Cloudflare) renew automatically with no action required.",[533,540,542],{"question":541},"What happens if my SSL certificate expires?",[18,543,544],{},"Visitors will see a security warning like \"Your connection is not private\" or \"NET::ERR_CERT_DATE_INVALID\". Most browsers will block access unless users click through warnings. This causes immediate loss of traffic and severely damages user trust.",[533,546,548],{"question":547},"Why did my automatic renewal fail?",[18,549,550,551,554],{},"Common causes include: port 80 blocked by firewall (needed for HTTP-01 challenge), web server stopped or misconfigured, DNS changes that broke domain verification, insufficient disk space, or Certbot/system not running. Check ",[31,552,553],{},"/var/log/letsencrypt/letsencrypt.log"," for specific errors.",[533,556,558],{"question":557},"Can I renew my certificate early?",[18,559,560,561,564],{},"Yes, use ",[31,562,563],{},"sudo certbot renew --force-renewal"," to renew immediately regardless of expiration date. However, Let's Encrypt has rate limits (50 certificates per domain per week), so don't force-renew unnecessarily.",[533,566,568],{"question":567},"Do I need to renew platform-managed certificates?",[18,569,570],{},"No. Vercel, Netlify, Cloudflare Pages, and similar platforms handle certificate renewal automatically. You don't need to take any action - just ensure your domain remains connected to the platform.",[533,572,574],{"question":573},"How do I switch from manual to automatic renewal?",[18,575,576,577,579,580,583],{},"If you originally set up certificates manually, you can still enable auto-renewal. Ensure Certbot's systemd timer or cron job is configured, then run ",[31,578,33],{}," to verify it works. The renewal configuration is stored in ",[31,581,582],{},"/etc/letsencrypt/renewal/",".",[585,586,589],"cta-box",{"href":587,"label":588},"/","Start Free Scan",[18,590,591],{},"Run a free security scan to check certificate validity and get expiration alerts.",[593,594,595,601,606,611],"related-articles",{},[596,597],"related-card",{"description":598,"href":599,"title":600},"Complete guide to initial SSL certificate installation.","/blog/how-to/https-setup","How to Set Up HTTPS",[596,602],{"description":603,"href":604,"title":605},"Configure SSL for custom domains on hosting platforms.","/blog/how-to/custom-domain-ssl","SSL for Custom Domains",[596,607],{"description":608,"href":609,"title":610},"Resolve HTTP resources breaking your HTTPS security.","/blog/how-to/mixed-content-fix","Fix Mixed Content Warnings",[596,612],{"description":613,"href":614,"title":615},"Configure HSTS, CSP, and other security headers.","/blog/how-to/add-security-headers","Add Security Headers",{"title":167,"searchDepth":617,"depth":617,"links":618},2,[619,620,621,627,633,638,639,645,649],{"id":38,"depth":617,"text":39},{"id":58,"depth":617,"text":59},{"id":147,"depth":617,"text":148,"children":622},[623,625,626],{"id":156,"depth":624,"text":157},3,{"id":173,"depth":624,"text":174},{"id":186,"depth":624,"text":187},{"id":193,"depth":617,"text":194,"children":628},[629,630,631,632],{"id":199,"depth":624,"text":200},{"id":211,"depth":624,"text":212},{"id":223,"depth":624,"text":224},{"id":248,"depth":624,"text":249},{"id":258,"depth":617,"text":259,"children":634},[635,636,637],{"id":264,"depth":624,"text":265},{"id":279,"depth":624,"text":280},{"id":297,"depth":624,"text":298},{"id":328,"depth":617,"text":329},{"id":369,"depth":617,"text":370,"children":640},[641,642,643,644],{"id":375,"depth":624,"text":376},{"id":387,"depth":624,"text":388},{"id":399,"depth":624,"text":400},{"id":411,"depth":624,"text":412},{"id":418,"depth":617,"text":419,"children":646},[647,648],{"id":422,"depth":624,"text":423},{"id":432,"depth":624,"text":433},{"id":442,"depth":617,"text":443,"children":650},[651,652,653,654,655,656,657],{"id":446,"depth":624,"text":447},{"id":456,"depth":624,"text":457},{"id":466,"depth":624,"text":467},{"id":476,"depth":624,"text":477},{"id":486,"depth":624,"text":487},{"id":496,"depth":624,"text":497},{"id":506,"depth":624,"text":507},"how-to","2026-01-08","Step-by-step guide to SSL certificate renewal. Set up automatic renewal with Certbot, monitor expiration dates, and troubleshoot common renewal failures.",false,"md",[664,666,668],{"question":535,"answer":665},"Let's Encrypt certificates expire every 90 days (Certbot renews at 60 days). Commercial certificates typically last 1-2 years. Platform-managed certificates (Vercel, Netlify) are renewed automatically.",{"question":541,"answer":667},"Visitors will see a security warning like 'Your connection is not private' and most browsers will block access to your site. This causes immediate loss of traffic and damages user trust.",{"question":547,"answer":669},"Common causes include: port 80 blocked by firewall, web server not running during renewal, DNS changes, or disk space issues. Check Certbot logs at /var/log/letsencrypt/ for details.","yellow",null,{},true,"Automatic SSL renewal setup, expiration monitoring, and troubleshooting guide.","/blog/how-to/certificate-renewal","[object Object]","HowTo",{"title":5,"description":660},{"loc":675},"blog/how-to/certificate-renewal",[],"summary_large_image","nzT0e601kwSkN5mZrti6mfbKpQpOH7fqgzvVvxpP6Y0",1775843928900]