Web Servers Intermediate

Fixing Let’s Encrypt & Certbot Verification Failures (HTTP-01 / DNS-01 Challenges)

Comprehensive troubleshooting guide for Certbot failed to authenticate some domains (HTTP-01 / 403 Forbidden). Learn root causes, system traces, and verified resolutions.

👨‍💻
Senior Systems Architect • Verified in Staging Labs

Comprehensive troubleshooting guide for Certbot failed to authenticate some domains (HTTP-01 / 403 Forbidden). Learn root causes, system traces, and verified resolutions.

When attempting to obtain or renew an SSL/TLS certificate using Certbot with Let's Encrypt, encountering an "ACME directory verification failed" error, particularly related to server connection issues, indicates a critical breakdown in the challenge process. This usually means that the Let's Encrypt validation server cannot connect to your server to verify domain ownership, preventing certificate issuance. This guide provides an in-depth, systematic approach to diagnose and resolve these connection-related verification failures on CentOS Stream and Rocky Linux environments.

LET’S ENCRYPT ACME VALIDATION & SSL HANDSHAKE FLOW
<!-- Origin Server -->
<rect x="35" y="80" width="190" height="155" rx="6" fill="#151926" stroke="#06b6d4" stroke-width="1.5"/>
<text x="130" y="110" fill="#06b6d4" font-size="14" font-weight="700" text-anchor="middle">Origin Server (Certbot)</text>
<text x="130" y="132" fill="#9ca3af" font-size="11" text-anchor="middle">ACME Client Daemon</text>
<rect x="50" y="150" width="160" height="65" rx="4" fill="#0b0d14" stroke="#2d3748" stroke-width="1"/>
<text x="130" y="172" fill="#67e8f9" font-size="10" font-family="'JetBrains Mono', monospace" text-anchor="middle">/.well-known/acme-challenge/</text>
<text x="130" y="190" fill="#9ca3af" font-size="10" text-anchor="middle">HTTP-01 / DNS-01 Token</text>

<!-- Arrow 1 -->
<path d="M 225 155 L 305 155" stroke="#f59e0b" stroke-width="2" stroke-dasharray="4,4"/>
<text x="265" y="145" fill="#f59e0b" font-size="10" text-anchor="middle">Challenge</text>

<!-- Let's Encrypt CA -->
<rect x="310" y="80" width="180" height="155" rx="6" fill="#151926" stroke="#f59e0b" stroke-width="1.5"/>
<text x="400" y="110" fill="#f59e0b" font-size="14" font-weight="700" text-anchor="middle">Let's Encrypt CA</text>
<text x="400" y="132" fill="#9ca3af" font-size="11" text-anchor="middle">Automated Validation Authority</text>
<rect x="325" y="150" width="150" height="65" rx="4" fill="#0b0d14" stroke="#2d3748" stroke-width="1"/>
<text x="400" y="172" fill="#fde68a" font-size="10" text-anchor="middle">Validates HTTP/DNS Record</text>
<text x="400" y="190" fill="#34d399" font-size="10" text-anchor="middle">Signs X.509 Certificate</text>

<!-- Arrow 2 -->
<path d="M 490 155 L 565 155" stroke="#10b981" stroke-width="2"/>
<text x="528" y="145" fill="#10b981" font-size="10" text-anchor="middle">Issued</text>

<!-- Nginx Proxy -->
<rect x="570" y="80" width="195" height="155" rx="6" fill="#151926" stroke="#10b981" stroke-width="1.5"/>
<text x="667" y="110" fill="#10b981" font-size="14" font-weight="700" text-anchor="middle">Nginx / SSL Termination</text>
<text x="667" y="132" fill="#9ca3af" font-size="11" text-anchor="middle">TLS 1.2 / TLS 1.3 Strict</text>
<rect x="585" y="150" width="165" height="65" rx="4" fill="#0b0d14" stroke="#10b981" stroke-width="1"/>
<text x="667" y="172" fill="#34d399" font-size="10" font-family="'JetBrains Mono', monospace" text-anchor="middle">fullchain.pem + privkey.pem</text>
<text x="667" y="190" fill="#9ca3af" font-size="10" text-anchor="middle">nginx -s reload (post-hook)</text>

Figure 4: Automated SSL certificate issuance and origin reverse proxy reload workflow.

Symptom & Error Signature

Users typically experience this issue when running certbot renew or certbot certonly (or run). The output will show a failure during the challenge phase, often accompanied by messages pointing to network connectivity or server reachability.

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator apache, Installer apache
Requesting a certificate for example.com and www.example.com

Certbot failed to authenticate some domains (authenticator: apache). The Certificate Authority reported these problems:
  Domain: example.com
  Type:   connection
  Detail: 192.0.2.1: Fetching http://example.com/.well-known/acme-challenge/TOKEN: Connection refused

  Domain: www.example.com
  Type:   connection
  Detail: 192.0.2.1: Fetching http://www.example.com/.well-known/acme-challenge/TOKEN: Connection refused

Hint: The Certificate Authority failed to download the temporary file created by Certbot. Ensure that the domain points to this machine's IP address and that your webserver is accessible from the internet.

Some challenges have failed.

Another common variant, especially if the web server is not running or misconfigured for the challenge:

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator webroot, Installer webroot
Requesting a certificate for example.com

Certbot failed to authenticate some domains (authenticator: webroot). The Certificate Authority reported these problems:
  Domain: example.com
  Type:   unauthorized
  Detail: Invalid response from http://example.com/.well-known/acme-challenge/TOKEN [192.0.2.1]: 404

Hint: The Certificate Authority failed to download the temporary file created by Certbot. Ensure that the domain points to this machine's IP address and that your webserver is accessible from the internet.

While the second example shows a 404 (Unauthorized) error, it still stems from the Let's Encrypt server successfully connecting but not finding the file, which can be due to a misconfigured web server or webroot path, or a proxy interfering. The "Connection refused" or "Connection timed out" are direct indicators of server reachability issues.

Root Cause Analysis

The "ACME directory verification failed server connection" error fundamentally means the Let's Encrypt validation servers cannot reach your server over HTTP (port 80) or HTTPS (port 443) to perform the challenge. Several common culprits can lead to this:

  1. Firewall Restrictions:

    • Local Firewall (firewalld): firewalld on CentOS Stream / Rocky Linux might be blocking incoming connections on port 80 (HTTP) or 443 (HTTPS).
    • Cloud Provider Firewall: Security Groups (AWS), Network Security Groups (Azure), Firewall Rules (GCP), or similar mechanisms at your cloud hosting provider might be blocking access to your instance.
    • Upstream/Hardware Firewall: If your server is behind a physical firewall or router, it might be blocking ports.
  2. DNS Resolution Issues:

    • Incorrect A/AAAA Records: Your domain's A (IPv4) or AAAA (IPv6) records might be pointing to an incorrect IP address or a server that is not currently online.
    • DNS Propagation Delays: Recent DNS changes might not have fully propagated across the internet, causing Let's Encrypt to query an outdated IP.
  3. Web Server Status & Configuration:

    • Web Server Not Running: Nginx or Apache HTTPD service might be stopped or failed.
    • Incorrect Listener: The web server might not be configured to listen on port 80 (or 443, if using TLS-ALPN-01 challenge, which is less common for initial setup).
    • Virtual Host Misconfiguration: The ServerName or ServerAlias in your virtual host configuration might not match the domain Certbot is trying to validate.
    • Document Root/Webroot Issues: If using webroot authenticator, the specified webroot path might be incorrect, or the web server might not have proper permissions to serve files from that directory.
  4. SELinux Policy Enforcement:

    • SELinux (Security-Enhanced Linux) might be preventing your web server process (e.g., nginx, httpd) from accessing the temporary files Certbot creates in the webroot or .well-known/acme-challenge directory.
  5. Network Connectivity Problems:

    • Network Interface Issues: The server's network interface might be down or misconfigured.
    • Router/Gateway Problems: Issues with the default gateway or upstream network devices.
    • CDN/Proxy Interference: If using a CDN or reverse proxy in front of your server, it might be misconfigured to block the ACME challenge path or redirect it incorrectly.
  6. Let's Encrypt Rate Limits: While less common for connection errors, repeatedly failing challenges can lead to temporary rate limiting, though this typically manifests as specific rate limit errors rather than connection refusals.

Step-by-Step Resolution

Follow these steps systematically to diagnose and resolve the connection issues preventing Certbot verification.

1. Verify DNS Resolution

Ensure your domain points to the correct public IP address of your server.

  1. Identify Your Server's Public IP:

    curl ifconfig.me
    # Or, if you have a public interface configured:
    ip a | grep -E 'inet .*scope global' | awk '{print $2}' | cut -d/ -f1
    
  2. Query DNS Records for Your Domain:

    dig A example.com +short
    dig AAAA example.com +short # Check AAAA if your server is IPv6 enabled
    dig A www.example.com +short
    

    Compare the IPs returned by dig with your server's public IP. They should match.

    If you recently updated DNS records, allow up to 24-48 hours for full propagation, though usually, it's much faster. You can use online tools like whatsmydns.net to check global propagation status.

2. Check Firewall Configuration

This is one of the most common causes. Let's Encrypt needs to reach your server on port 80 (for http-01 challenge) or 443 (for tls-alpn-01 challenge).

  1. Local Firewall (firewalld): CentOS Stream and Rocky Linux use firewalld by default.

    • List current active zones and services/ports:

      sudo firewall-cmd --list-all --zone=public
      

      Look for http or 80/tcp and https or 443/tcp in the services or ports list.

    • If port 80 or 443 is missing, add them:

      sudo firewall-cmd --zone=public --add-service=http --permanent
      sudo firewall-cmd --zone=public --add-service=https --permanent
      sudo firewall-cmd --reload
      
    • Verify again:

      sudo firewall-cmd --list-all --zone=public
      
  2. Cloud Provider Firewall / Security Groups: If your server is hosted on a cloud platform (AWS, Azure, GCP, DigitalOcean, Linode, etc.), check its network security configuration.

    • AWS: Check associated Security Groups for inbound rules allowing HTTP (port 80) and HTTPS (port 443) from 0.0.0.0/0 (anywhere).
    • Azure: Check Network Security Groups (NSG) inbound security rules.
    • GCP: Check Firewall Rules.
    • Other VPS providers: Look for "Firewall" or "Network" settings in your control panel.

    Ensure that both your local firewall and any cloud provider firewalls are correctly configured. A single blocked port at any layer will cause failure.

  3. Test External Connectivity: From a machine other than your server, try to connect to your domain on port 80.

    curl -v http://example.com
    # Or to test raw port connectivity:
    nc -vz example.com 80
    

    If curl hangs or nc reports "Connection refused" or "Connection timed out", your firewall or network is likely blocking access.

3. Inspect Web Server Status and Configuration

Certbot needs your web server (Nginx or Apache) to be running and correctly configured to serve the ACME challenge files.

  1. Check Web Server Status:

    • For Nginx:
      sudo systemctl status nginx
      
    • For Apache HTTPD:
      sudo systemctl status httpd
      

    Ensure the service is active (running). If not, start it:

    sudo systemctl start nginx # or httpd
    sudo systemctl enable nginx # or httpd (to start on boot)
    
  2. Test Web Server Configuration Syntax:

    • For Nginx:
      sudo nginx -t
      
    • For Apache HTTPD:
      sudo apachectl configtest
      

    Address any syntax errors reported before proceeding.

  3. Verify Web Server Listening Ports: Confirm that your web server is listening on port 80 (and 443).

    sudo ss -tulnp | grep -E ":80|:443"
    

    You should see nginx or httpd listening on 0.0.0.0:80 (or :::80 for IPv6) and potentially 0.0.0.0:443.

  4. Test ACME Challenge Path (Manual): Certbot places a temporary file in /.well-known/acme-challenge/. You can manually simulate this.

    • Create a dummy file:
      sudo mkdir -p /var/www/html/.well-known/acme-challenge/ # Adjust path to your webroot
      echo "test" | sudo tee /var/www/html/.well-known/acme-challenge/testfile
      
    • Verify webroot configuration in Nginx/Apache: Ensure your server block or virtual host for example.com has the correct root directive (Nginx) or DocumentRoot (Apache).
      • Nginx Example (/etc/nginx/conf.d/example.com.conf or similar):
        server {
            listen 80;
            server_name example.com www.example.com;
            root /var/www/html; # Ensure this path is correct
            # ... other directives ...
        }
        
      • Apache Example (/etc/httpd/conf.d/example.com.conf or similar):
        <VirtualHost *:80>
            ServerName example.com
            ServerAlias www.example.com
            DocumentRoot /var/www/html # Ensure this path is correct
            <Directory "/var/www/html">
                AllowOverride All
                Require all granted
            </Directory>
            # ... other directives ...
        </VirtualHost>
        
    • Access the test file from your browser or curl (external connection):
      curl http://example.com/.well-known/acme-challenge/testfile
      
      You should see test as the output. If you get a 404, 403, or connection error, your web server configuration or permissions are likely at fault. Delete the test file afterwards.
      sudo rm /var/www/html/.well-known/acme-challenge/testfile
      

    If using a CDN or reverse proxy, ensure it's configured to pass through requests for /.well-known/acme-challenge/ directly to your origin server without caching or redirection. Some CDNs offer specific settings for Let's Encrypt validation.

4. Temporarily Disable SELinux (for testing)

SELinux can prevent the web server from reading or writing files in certain directories, especially if Certbot places them in non-standard locations or if your webroot has unusual permissions.

  1. Check SELinux Status:

    sestatus
    

    If SELinux status is enabled and Current mode is enforcing, it might be the cause.

  2. Temporarily Set to Permissive Mode:

    sudo setenforce 0
    

    This changes SELinux to permissive mode, logging but not enforcing policy. Try running Certbot again.

  3. If Certbot succeeds with SELinux permissive: This indicates an SELinux issue. Re-enable enforcing mode immediately:

    sudo setenforce 1
    

    Then, investigate and apply appropriate SELinux policies rather than leaving it disabled or in permissive mode. Common fixes include:

    • Restoring file contexts:
      sudo restorecon -Rv /var/www/html # Adjust path to your webroot
      
    • Checking boolean settings:
      sudo getsebool -a | grep httpd
      # Common ones: httpd_can_network_connect, httpd_unified
      # To set: sudo setsebool -P httpd_can_network_connect on
      
    • Review your /var/log/audit/audit.log for SELinux denial messages (AVC errors). Use ausearch to help interpret these.

    Do NOT leave SELinux in permissive or disabled mode permanently on a production server. It is a critical security feature.

5. Review Certbot Command & Logs

Double-check your Certbot command and review its detailed logs.

  1. Examine Certbot Command: Ensure the domain names provided to Certbot (-d example.com -d www.example.com) exactly match the domains configured in your DNS and web server. If you're using webroot, ensure the --webroot-path is correct for the domain.

  2. Check Certbot Logs: The most detailed information is in the Certbot logs.

    sudo tail -f /var/log/letsencrypt/letsencrypt.log
    # Look for specific errors or stack traces around the "ACME" or "connection" failure.
    
  3. Use certbot --dry-run: This command attempts to simulate the certificate issuance process without actually modifying your server or requesting a real certificate, which is useful for testing configurations without hitting rate limits.

    sudo certbot renew --dry-run
    # Or for a fresh certificate attempt:
    sudo certbot certonly --nginx -d example.com --dry-run
    sudo certbot certonly --apache -d example.com --dry-run
    sudo certbot certonly --webroot -w /var/www/html -d example.com --dry-run
    

6. Network Connectivity and Proxies

If all local configurations seem correct, investigate broader network issues.

  1. Traceroute to Let's Encrypt API: Ensure your server can reach the Let's Encrypt validation servers.

    traceroute acme-v02.api.letsencrypt.org
    # Or, if traceroute isn't installed:
    mtr acme-v02.api.letsencrypt.org
    

    Look for abnormally high latency or dropped packets, which could indicate upstream network problems.

  2. CDN/Reverse Proxy Configuration: If your server is behind a CDN (like Cloudflare) or a reverse proxy (like another Nginx instance, Varnish, HAProxy), ensure it's configured to:

    • Pass through requests for /.well-known/acme-challenge/ to your origin server without caching.
    • Not block requests from the Let's Encrypt validation IP ranges. Many CDNs have specific settings to handle Let's Encrypt challenges. For Cloudflare, you might need to temporarily disable proxying (set DNS record to "DNS Only" / grey cloud) during renewal if using http-01 challenge, or use their native integration/API for validation (dns-01 challenge).

7. Update System and Certbot

Outdated Certbot versions or system packages can sometimes lead to unexpected issues.

  1. Update System Packages:

    sudo dnf update -y
    
  2. Update Certbot: If Certbot was installed via EPEL or Snap, it should update with dnf. If installed via pip in a virtual environment, activate the environment and run:

    pip install --upgrade certbot certbot-nginx # or certbot-apache, certbot-dns-cloudflare, etc.
    

By systematically working through these steps, you should be able to pinpoint the exact cause of your "Certbot ACME directory verification failed server connection" error and successfully secure your domain.

Multi-Platform Step-by-Step Implementation Matrix

The core underlying mechanism is identical across UNIX distributions, but configuration paths, daemon names, and kernel socket limits differ by platform. Follow the exact instructions for your target operating system below.

🐧 Debian 12 (Bookworm) & Ubuntu 24.04 / 22.04 LTS

On Debian-based systems, systemd service units and standard apt package paths apply:

# Verify active listening sockets and associated process IDs
sudo ss -tulpn | grep -E ":(80|443|3306|5432|6379)"

# Inspect live systemd daemon status
sudo systemctl status --no-pager nginx mysql postgresql redis

🔴 Rocky Linux 9, AlmaLinux & CentOS Stream

On RHEL-derived distributions, remember to check SELinux boolean contexts and firewalld rules:

# Check if SELinux is enforcing and denying network binds
sudo getenforce
sudo setsebool -P httpd_can_network_connect 1

# Open firewall ports
sudo firewall-cmd --permanent --add-service=http --add-service=https
sudo firewall-cmd --reload

🍎 macOS Local Environment (Homebrew)

On macOS, Homebrew services run in user space or launchd:

# Identify processes occupying ports on macOS
sudo lsof -iTCP -sTCP:LISTEN -n -P

# Restart Homebrew services
brew services restart nginx

🪟 Windows WSL2 (Ubuntu / Debian)

On WSL2, port forwarding bridges the Windows host with the WSL virtual adapter. Always ensure host port sharing is not blocked by Windows Fast Startup or native IIS/Hyper-V services:

# Check ports on Windows Host (PowerShell as Admin)
Get-NetTCPConnection -State Listen | Where-Object { $_.LocalPort -in 80,443,3306,5432 }
👨‍💻

Johnathon Wheeler

Senior Systems Architect & DevOps Engineer • Austin, TX

Connect on LinkedIn

Johnathon has over 16 years of hands-on experience designing, debugging, and scaling Linux web hosting stacks, container clusters, and high-availability database architectures. Every guide on ButItWorkedLocal is independently tested against Debian 12, Ubuntu 24.04/22.04 LTS, Rocky Linux, and Docker environments to guarantee reproducibility in production.

🛡️

Our Production Verification Guarantee

Encountering a bug not covered here or running a non-standard kernel configuration? Our solutions are continually refined against real production incidents. Submit an environment trace for our editorial team to replicate.