Enabling HTTPS with a Let's Encrypt certificate protects logins, cookies, and API traffic from interception and tampering, while also eliminating modern browser “Not Secure” warnings. It also unlocks safer defaults like HTTP→HTTPS redirects and (once stable) HSTS for stricter transport security.

Let's Encrypt issues short-lived (90-day) certificates via the ACME protocol, typically using an HTTP-01 challenge on port 80 to prove domain control. certbot can request the certificate, write the Apache SSL virtual host configuration, store keys and certificates under /etc/letsencrypt, and keep renewals automated via systemd timers.

A correct DNS setup and real inbound reachability matter more than local tests, especially when a firewall, reverse proxy, or CDN sits in front of Apache. A broken .AAAA record (IPv6) can also cause validation failure even when IPv4 works, because the CA may try both. Treat Apache vhost edits as a production change, validate config syntax before reloads, and confirm automated renewal before enabling stricter policies like HSTS.

Steps to configure Let's Encrypt SSL in Apache:

  1. Confirm that the domain resolves to the server public IP address.
    $ getent ahosts example.com | head -n 2
    203.0.113.10   STREAM example.com
    203.0.113.10   DGRAM  example.com
  2. Confirm that Apache serves the site over HTTP on port 80.
    $ curl -I http://example.com
    HTTP/1.1 200 OK
    Server: Apache/2.4.57 (Debian)
    ##### snipped #####
  3. Confirm that the Apache virtual host selection matches the intended site for the domain.
    $ sudo apache2ctl -S
    VirtualHost configuration:
    *:80                   example.com (/etc/apache2/sites-enabled/example.com.conf:1)
    ServerRoot: "/etc/apache2"
    ##### snipped #####

    Multiple vhosts with overlapping ServerName or ServerAlias entries can cause certbot to edit the wrong site.

  4. Install certbot and the Apache plugin on Debian-style systems.
    $ sudo apt update
    Hit:1 http://deb.debian.org/debian bookworm InRelease
    ##### snipped #####
    $ sudo apt install --assume-yes certbot python3-certbot-apache
    ##### snipped #####

    On RHEL-family systems, install with dnf install certbot python3-certbot-apache when available.

  5. Run certbot with the Apache plugin for the domain.
    $ sudo certbot --apache -d example.com -d www.example.com
    Saving debug log to /var/log/letsencrypt/letsencrypt.log
    Requesting a certificate for example.com and www.example.com
    ##### snipped #####
    Successfully received certificate.
    Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem
    Key is saved at:         /etc/letsencrypt/live/example.com/privkey.pem
    Deploying certificate
    ##### snipped #####

    Use --test-cert for trial runs to avoid Let's Encrypt rate limits, and select the redirect option only when HTTP can safely be forced to HTTPS.

  6. Validate the Apache configuration syntax after the SSL vhost change.
    $ sudo apache2ctl configtest
    Syntax OK

    Reloading with a broken SSL vhost can take HTTPS offline for the entire server.

  7. Reload the Apache service to apply the SSL configuration.
    $ sudo systemctl reload apache2
  8. Verify that the Apache service is active after the reload.
    $ sudo systemctl status apache2 --no-pager
    ● apache2.service - The Apache HTTP Server
         Loaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor preset: enabled)
         Active: active (running) since Sat 2025-12-13 11:58:04 UTC; 2min ago
    ##### snipped #####
  9. Verify HTTPS responses for the site.
    $ curl -I https://example.com
    HTTP/2 200
    server: Apache/2.4.57 (Debian)
    ##### snipped #####
  10. Verify that HTTP redirects to HTTPS when redirect is enabled.
    $ curl -I http://example.com
    HTTP/1.1 301 Moved Permanently
    Location: https://example.com/
    ##### snipped #####
  11. Verify the certificate chain and expiry served by the site using openssl.
    $ echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -noout -issuer -subject -dates
    issuer=C=US,O=Let's Encrypt,CN=R3
    subject=CN=example.com
    notBefore=Dec 13 00:00:00 2025 GMT
    notAfter=Mar 13 23:59:59 2026 GMT
  12. Validate that the certbot renewal timer is active.
    $ sudo systemctl status certbot.timer --no-pager
    ● certbot.timer - Run certbot twice daily
         Loaded: loaded (/lib/systemd/system/certbot.timer; enabled; vendor preset: enabled)
         Active: active (waiting) since Sat 2025-12-13 12:00:00 UTC; 1min ago
        Trigger: Sat 2025-12-13 12:19:02 UTC
    ##### snipped #####
  13. Perform a renewal dry run to confirm automated renewal works.
    $ sudo certbot renew --dry-run
    Saving debug log to /var/log/letsencrypt/letsencrypt.log
    ##### snipped #####
    Congratulations, all simulated renewals succeeded:
      /etc/letsencrypt/live/example.com/fullchain.pem (success)
Discuss the article:

Comment anonymously. Login not required.