SSL certificate renewal nightmare!
Our main SaaS site is DOWN due to an expired SSL certificate, and I'm completely stuck trying to fix it. It's been hours, and the site is unreachable via HTTPS. This is an absolute nightmare, and I'm desperate for some help.
We run a small SaaS analytics dashboard hosted on a DigitalOcean VPS with Nginx. Our SSL was managed by Let's Encrypt, set up for auto-renewal months ago, and it has worked flawlessly until now. I woke up to `NET::ERR_CERT_DATE_INVALID` errors across all browsers, and after checking, I confirmed the SSL certificate renewal failed; it's expired.
Here's what I've tried, and everything has failed:
- Ran `sudo certbot renew --force-renewal` multiple times. It consistently fails.
- Manually tried `sudo certbot --nginx -d example.com`. Same failure.
- Verified DNS records (A and CNAME) for `example.com` and `www.example.com` are correctly pointing to the server IP. I've used `dig` and various online DNS checkers โ everything *looks* correct.
- Checked Nginx config for any misconfigurations or conflicting server blocks. All seems fine, pointing to the correct certificate paths.
- Looked at firewall (UFW) rules; port 80 and 443 are open.
- Even tried revoking the old certificate and reissuing from scratch, but it also hits the same wall.
The specific error message I keep getting is:
Challenge failed for domain example.com
Attempting to renew cert (example.com) from /etc/letsencrypt/renewal/example.com.conf produced an unexpected error: Failed authorization procedure. example.com (http-01): urn:ietf:params:acme:error:connection :: The server could not connect to the client to verify the domain :: Fetching http://example.com/.well-known/acme-challenge/SOME_TOKEN: Connection refused.
Skipping.
All renewal attempts failed. The following certs could not be renewed:
/etc/letsencrypt/live/example.com/fullchain.pem (failure)
I don't understand why the `http-01` challenge is failing with "Connection refused" when port 80 is open and DNS records seem correct. Is there some caching issue, a weird DNS propagation problem I'm missing, or an Nginx misconfiguration I'm just not seeing?
Please, any ideas? We need to get this site back online ASAP. What else can I check? What are common reasons for `http-01` challenge "Connection refused" when everything else seems fine?
2 Answers
MD Alamgir Hossain Nahid
Answered 13 hours agoHey Camila Cruz, I completely understand how frustrating and critical an expired SSL certificate can be, especially for a SaaS platform. This exact scenario has caused me headaches on past projects โ it's a major disruption to your SaaS growth and user trust.
The "Connection refused" error during the `http-01` challenge is a very specific clue. It means the Let's Encrypt validation server tried to connect to your `example.com` on port 80 and was actively rejected. This isn't a timeout; it's an explicit refusal. While you've checked UFW, Nginx config, and DNS, there are a few common culprits for this particular error that are often overlooked.
Let's break down the most likely causes and how to troubleshoot them:
- Verify Nginx is Actively Listening on Port 80 for the Domain:
- Check Nginx Status: First, ensure Nginx is running:
sudo systemctl status nginx. If it's not active, start it:sudo systemctl start nginx. - Port 80 Listener: Even if UFW shows port 80 open, Nginx itself might not be configured to listen on it, or a misconfiguration might prevent it from responding. Use
sudo netstat -tuln | grep ':80'to confirm Nginx is listening on port 80. You should see an entry liketcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN. - Nginx Server Block for Port 80: Open your Nginx configuration files (typically in
/etc/nginx/sites-available/example.comor/etc/nginx/nginx.conf). Look for aserverblock that includeslisten 80;. It's crucial that this block exists and is active. - Aggressive HTTP to HTTPS Redirect: A very common issue is an Nginx configuration that immediately redirects all HTTP traffic to HTTPS, even the
/.well-known/acme-challenge/path.server { listen 80; server_name example.com www.example.com; # This redirect is the culprit if it's placed before the .well-known location return 301 https://$host$request_uri; }Certbot needs to access
http://example.com/.well-known/acme-challenge/directly on port 80. You need alocationblock that allows this specific path to be served over HTTP before any general HTTP to HTTPS redirect. Certbot usually handles this, but manual overrides or misconfigurations can break it. Ensure your port 80 server block looks something like this:server { listen 80; server_name example.com www.example.com; location /.well-known/acme-challenge { root /var/www/html; # Or whatever your webroot is try_files $uri =404; } # Only redirect to HTTPS AFTER handling the ACME challenge path location / { return 301 https://$host$request_uri; } }After any changes, always run
sudo nginx -tto test the configuration syntax, thensudo systemctl reload nginx.
- Check Nginx Status: First, ensure Nginx is running:
- DigitalOcean Cloud Firewall:
- You mentioned UFW, but DigitalOcean also has its own Cloud Firewalls that operate at the droplet level, *before* traffic even reaches your VPS's UFW. Log into your DigitalOcean account, navigate to your droplet, and check its associated firewall rules. Ensure that inbound TCP traffic on port 80 and 443 is explicitly allowed from all IPs (0.0.0.0/0). This is a very common oversight for "Connection refused" errors.
- Test Port 80 Accessibility Externally:
- From your local machine or another server, try to access a simple HTTP resource. For example, if you place an
index.htmlfile in your Nginx webroot (e.g.,/var/www/html/index.html), can you access it viahttp://example.com/index.html? If not, you'll likely get a "Connection refused" or "Connection reset" error, confirming the issue is before Nginx can even process the request. - You can also try
curl -v http://example.com/from an external machine to see the exact connection attempt and its failure.
- From your local machine or another server, try to access a simple HTTP resource. For example, if you place an
- DNS Propagation (Re-check with Global Tools):
- While
digon your server might show correct records, it's worth double-checking with a global DNS checker like whatsmydns.net to ensure all major resolvers are pointing to your DigitalOcean IP for bothexample.comandwww.example.com. Sometimes local DNS caches can be misleading.
- While
- Nginx Error Logs:
- Check Nginx's error logs (typically
/var/log/nginx/error.log) immediately after attempting acertbot renewcommand. Any Nginx-related issues preventing it from serving the challenge will often show up there.
- Check Nginx's error logs (typically
- Try a Certbot Dry Run:
- Instead of
--force-renewal, which can hit rate limits, trysudo certbot renew --dry-run. This simulates the renewal process and often provides more detailed output about where the failure occurs without actually attempting to modify certificates.
- Instead of
Given the specific "Connection refused" error for the `http-01` challenge, my strongest suspicion is either the DigitalOcean Cloud Firewall or an Nginx configuration issue where the port 80 server block is not correctly handling the /.well-known/acme-challenge/ path, likely due to an overly aggressive HTTP to HTTPS redirect. Addressing these should help you get your server postbacks and site back online quickly.
Camila Cruz
Answered 13 hours agoOh wow, perfect breakdown MD Alamgir Hossain Nahid! This is exactly what I needed to really deep dive into that "Connection refused" error, seriously appreciate the detailed troubleshooting steps here.
Super helpful for anyone running into this, looking forward to seeing more of your insights around here!