For our Blog Visitor only Get Additional 3 Month Free + 10% OFF on TriAnnual Plan YSBLOG10
Grab the Deal

How to Fix Let’s Encrypt on Linux Server for Failed SSL Renewals

To fix Let’s Encrypt on a Linux server, verify DNS points to the server, open ports 80/443, install Certbot via Snap, run the correct challenge method (HTTP-01 for regular domains, DNS-01 for wildcards), update web server configs, then test automatic renewal with certbot renew --dry-run and reload your web server.

If your SSL renewals fail or new certificates won’t issue, this step-by-step guide explains how to fix Let’s Encrypt on a Linux server using Certbot.

We’ll cover common errors, the right installation method, web server configuration for Apache and Nginx, DNS and firewall checks, renewal automation, and production-ready best practices.

Quick Diagnosis Checklist

Before deep troubleshooting, confirm these items. Most Let’s Encrypt failures trace back to one of them.

  • DNS A/AAAA records point to the correct server IP (avoid stale or wrong IPv6).
  • Ports 80 (HTTP) and 443 (HTTPS) are open and reachable from the internet.
  • Web server serves /.well-known/acme-challenge/ without redirects or blocks during HTTP-01 validation.
  • No restrictive CAA record blocking Let’s Encrypt issuance.
  • Server time is accurate (NTP enabled).
  • Using a current Certbot install (Snap recommended) and correct plugin (nginx, apache, webroot, or DNS).
  • Automatic renewal is enabled and healthy (systemd timer or cron).

How Let’s Encrypt Validation Works

Let’s Encrypt uses the ACME protocol to verify domain control. Understanding challenge types helps you pick the right fix.

HTTP-01 (Most Common)

ACME fetches a token from http://example.com/.well-known/acme-challenge/<token>. Your server must be publicly reachable on port 80 and serve the exact file content with a 200 status. Redirects to HTTPS are fine if the file is still reachable, but complex rewrites often break validation.

DNS-01 (Wildcards and Complex Setups)

ACME checks a TXT record at _acme-challenge.example.com. Choose this when issuing wildcard certificates or when HTTP is not possible due to proxies, locked-down ports, or custom routing. Use a DNS plugin to automate TXT updates.

TLS-ALPN-01 (Advanced)

Validates over port 443 with a special ALPN certificate. Useful behind certain proxies, but not as beginner-friendly as HTTP-01 or DNS-01.

Install or Repair Certbot the Right Way

The most reliable method today is the Snap package. Many distro packages are outdated and cause failures.

# Remove old distro Certbot if present
sudo apt remove certbot python3-certbot-* -y 2>/dev/null || true
sudo dnf remove certbot python3-certbot-* -y 2>/dev/null || true
sudo yum remove certbot python3-certbot-* -y 2>/dev/null || true

# Install Snap (if not installed)
sudo apt update && sudo apt install -y snapd || true
sudo dnf install -y snapd || true
sudo systemctl enable --now snapd

# Install Certbot via Snap and link it
sudo snap install core; sudo snap refresh core
sudo snap install --classic certbot
sudo ln -sf /snap/bin/certbot /usr/bin/certbot

# Verify version (should be recent)
certbot --version

Choose the plugin that matches your stack:

  • Nginx: certbot --nginx (auto config)
  • Apache: certbot --apache (auto config)
  • Webroot (custom paths/proxies): certbot certonly --webroot -w /var/www/html -d example.com -d www.example.com
  • Standalone (no web server running): certbot certonly --standalone -d example.com

Fix Common Let’s Encrypt Errors (With Proven Solutions)

Connection Refused or Timeout on Port 80/443

Symptoms: logs show “Connection refused”, “Timeout during connect”, or “Fetching …: Timeout”.

  • Open the firewall:
# UFW
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw reload

# firewalld
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
  • Check cloud security groups or provider firewalls (AWS, GCP, Azure, DigitalOcean).
  • Ensure Nginx/Apache is running and listening on the correct IPs.

404 or 403 on .well-known/acme-challenge

Cause: rewrite rules or permissions block access to the challenge path.

Fix for Nginx: add an explicit location that bypasses redirects and points to the correct webroot.

server {
    listen 80;
    server_name example.com www.example.com;

    root /var/www/html;

    # Always allow ACME challenge
    location ^~ /.well-known/acme-challenge/ {
        default_type "text/plain";
        allow all;
        root /var/www/html;
    }

    # Optional: redirect others to HTTPS
    location / {
        return 301 https://$host$request_uri;
    }
}

Fix for Apache: ensure Alias and Directory config allows reads and is not blocked by rewrites.

<VirtualHost *:80>
  ServerName example.com
  ServerAlias www.example.com
  DocumentRoot /var/www/html

  Alias /.well-known/acme-challenge/ /var/www/html/.well-known/acme-challenge/
  <Directory "/var/www/html/.well-known/acme-challenge/">
    Options None
    AllowOverride None
    Require all granted
  </Directory>

  # If using .htaccess redirects, add an exception:
  # RewriteRule ^\.well-known/acme-challenge/ - [L]
</VirtualHost>

Permissions: the challenge file must be readable by the web server. For SELinux systems, restore contexts if needed:

sudo chown -R www-data:www-data /var/www/html
sudo find /var/www/html -type d -exec chmod 755 {} \;
sudo find /var/www/html -type f -exec chmod 644 {} \;
sudo restorecon -Rv /var/www/html 2>/dev/null || true

Wrong DNS, Especially IPv6

If you publish an AAAA record but your server is not actually listening on that IPv6 address, ACME may connect over IPv6 and fail. Either fix IPv6 on the host or remove the AAAA record. Verify with:

dig +short A example.com
dig +short AAAA example.com
curl -I http://example.com
curl -6 -I http://example.com  # test IPv6 path

Behind a CDN or Reverse Proxy (e.g., Cloudflare)

For HTTP-01, ensure the proxy passes requests to /.well-known/acme-challenge/ unmodified. Temporarily set DNS to “DNS only” (no proxy) during issuance, or use DNS-01 with the provider’s API plugin to avoid proxy issues entirely.

CAA Record Blocks Issuance

Check if you have restrictive CAA records. You must allow Let’s Encrypt:

dig CAA example.com

If present, include entries like 0 issue "letsencrypt.org". Otherwise issuance will be refused.

Clock Skew and NTP

ACME requires correct system time. Enable NTP and resync:

timedatectl
sudo timedatectl set-ntp true
sudo systemctl restart systemd-timesyncd 2>/dev/null || true

Rate Limits and Staging

If you request too many certs quickly, you may hit Let’s Encrypt rate limits. Test with the staging environment first:

certbot certonly --nginx -d example.com --test-cert --agree-tos -m admin@example.com

Once stable, run without --test-cert for production.

Set Up Reliable Auto-Renewal

Certbot via Snap installs a systemd timer for renewals. Validate it and perform a dry run.

systemctl list-timers | grep certbot
systemctl status snap.certbot.renew.service
sudo certbot renew --dry-run

Reload your web server automatically after successful renewals with a deploy hook:

# Nginx
sudo certbot renew --deploy-hook "systemctl reload nginx"

# Apache
sudo certbot renew --deploy-hook "systemctl reload apache2"   # Debian/Ubuntu
sudo certbot renew --deploy-hook "systemctl reload httpd"     # RHEL/CentOS/Alma

Certificates live in /etc/letsencrypt/live/<domain>/ as fullchain.pem and privkey.pem. Ensure your vhosts reference these paths and reload after issuance.

Migrating or Cleaning Up Broken Installs

Replace Outdated Certbot With Snap

If you used distro packages in the past, remove them and switch to Snap (as shown earlier). Certs in /etc/letsencrypt remain intact.

Sometimes moving servers or manual edits break symlinks. If ls -l /etc/letsencrypt/live/<domain> shows dangling links, reissue the certificate:

sudo mv /etc/letsencrypt/live/example.com /etc/letsencrypt/live/example.com.bak.$(date +%F)
sudo certbot certonly --nginx -d example.com -d www.example.com
# or use --apache / --webroot depending on your stack

Migrate Certificates Between Servers

Copy /etc/letsencrypt and maintain permissions (root:root, 600 for private keys). Then run a dry-run renewal to verify:

sudo rsync -a /etc/letsencrypt/ newserver:/etc/letsencrypt/
sudo certbot renew --dry-run
sudo systemctl reload nginx || sudo systemctl reload apache2 || sudo systemctl reload httpd

When to Use DNS-01 and How to Automate It

Use DNS-01 for wildcard certificates or when HTTP-01 is blocked by proxies, WAFs, or strict networks. Automate TXT record creation with a DNS plugin to avoid manual edits and long propagation windows.

# Example: Cloudflare (requires a credentials file)
sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /root/.secrets/cf.ini \
  -d example.com -d *.example.com

# For other providers, use the matching --dns-*** plugin.
# Alternative lightweight client:
curl https://get.acme.sh | sh
~/.acme.sh/acme.sh --issue --dns dns_cf -d example.com -d *.example.com

Nginx and Apache: Production-Ready SSL Config Snippets

Nginx Example

server {
    listen 80;
    server_name example.com www.example.com;
    root /var/www/html;

    location ^~ /.well-known/acme-challenge/ {
        default_type "text/plain";
        root /var/www/html;
    }

    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com www.example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Optional: ECDSA keys
    # ssl_certificate     /etc/letsencrypt/live/example.com-ecdsa/fullchain.pem;
    # ssl_certificate_key /etc/letsencrypt/live/example.com-ecdsa/privkey.pem;

    root /var/www/html;
    index index.html index.php;
}

Apache Example

<VirtualHost *:80>
  ServerName example.com
  ServerAlias www.example.com
  DocumentRoot /var/www/html
  RewriteEngine On
  RewriteRule ^\.well-known/acme-challenge/ - [L]
  RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]
</VirtualHost>

<IfModule mod_ssl.c>
<VirtualHost *:443>
  ServerName example.com
  ServerAlias www.example.com
  DocumentRoot /var/www/html

  SSLEngine on
  SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
  SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
</VirtualHost>
</IfModule>

Security Best Practices for Let’s Encrypt on Linux

  • Limit key access: chmod 600 on private keys; owner root:root.
  • Use ECDSA keys for better performance: certbot certonly --key-type ecdsa --elliptic-curve secp384r1.
  • Monitor expiry: add a lightweight check or use external monitors that alert before 30 days.
  • Pin to fullchain.pem in your web server, not just cert.pem, to avoid chain issues.
  • Keep Certbot updated via Snap; periodically run certbot renew --dry-run after major OS updates.

Why Fixing Let’s Encrypt With YouStable Is Easier

As a hosting provider, YouStable optimizes Linux servers for SSL from day one. Our managed stacks ship with properly configured firewalls, current Certbot, and tested Nginx/Apache templates, so certificates issue and renew reliably. If you’d rather not wrestle with ACME challenges and DNS plugins, our team can implement and monitor it for you.

Step-by-Step: From Zero to Working SSL

  • Point DNS A/AAAA records to your server IP and wait for propagation.
  • Open ports 80/443 on OS and provider firewall.
  • Install Certbot via Snap and verify version.
  • Choose the right plugin:
    • Simple sites: certbot --nginx or certbot --apache
    • Custom roots/proxies: --webroot
    • Wildcards or behind-CDN: DNS plugin
  • Confirm web server uses /etc/letsencrypt/live/<domain>/fullchain.pem and privkey.pem.
  • Reload Nginx/Apache; verify with curl -I https://example.com.
  • Enable and test renewals with certbot renew --dry-run.

FAQs

Why does Certbot say “Challenge failed” for HTTP-01?

Common causes are blocked port 80, wrong DNS target, forced HTTPS redirects that prevent access to /.well-known/acme-challenge/, or an incorrect webroot path. Make the challenge path publicly reachable over HTTP, verify DNS/IPv6, and retry with the correct plugin or --webroot path.

How do I renew Let’s Encrypt certificates automatically?

With Snap-installed Certbot, a systemd timer runs twice daily. Confirm with systemctl list-timers | grep certbot, run certbot renew --dry-run to test, and add a deploy hook to reload your web server after renewal.

Should I use HTTP-01 or DNS-01?

Use HTTP-01 for standard domain certificates when port 80 is open and the site serves files directly. Use DNS-01 for wildcard domains, behind-CDN setups, or when port 80 cannot be exposed. DNS-01 with a provider plugin automates TXT record creation.

How do I fix chain or “untrusted certificate” errors?

Point your web server to fullchain.pem (not just cert.pem). Reload Nginx/Apache. On very old clients, the trust store may be outdated; update the OS or use modern devices. Let’s Encrypt currently issues from ISRG Root X1.

Is there an alternative to Certbot?

Yes. Lightweight clients like acme.sh or lego work well and support many DNS providers. If you choose them, disable Certbot renewals to avoid conflicts and document your renewal process clearly.

Deepika Verma

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top