Let's Encrypt issues free, trusted TLS certificates that last 90 days, and certbot renews them for you. The certbot Apache plugin reads your virtual hosts, proves you control each domain, installs the certificate and can add the HTTP to HTTPS redirect. This guide covers Ubuntu, Debian, AlmaLinux and Rocky Linux.
Before you start
- Apache running with a virtual host for each domain, with
ServerNameandServerAliasset. See Set up Apache virtual hosts. - DNS A records for the domain and
wwwpointing at your server, for example203.0.113.10. Check withdig +short example.com. - Ports 80 and 443 open in your firewall. Let's Encrypt connects on port 80 to validate.
- On AlmaLinux and Rocky,
mod_sslinstalled (sudo dnf install mod_ssl).
Note: If the domain is proxied through Cloudflare (orange cloud), validation usually still works over HTTP, but switch the proxy off while you troubleshoot so you test your own server directly.
Install certbot and the Apache plugin
Ubuntu and Debian
The packaged version is fine for most servers:
sudo apt update
sudo apt install -y certbot python3-certbot-apacheAlmaLinux and Rocky Linux
Certbot comes from the EPEL repository:
sudo dnf install -y epel-release
sudo dnf install -y certbot python3-certbot-apacheThe certbot project also publishes a snap package that tracks the newest release. Either route works; do not install both. Check what you have with:
certbot --versionIssue a certificate for each site
Run certbot with the Apache plugin and name every host the certificate should cover:
sudo certbot --apache -d example.com -d www.example.comThe first time, certbot asks for an email address for expiry warnings and for you to accept the Let's Encrypt terms. It then:
- Finds the vhost whose
ServerNameorServerAliasmatches. - Places a temporary validation file and asks Let's Encrypt to fetch it over port 80.
- Saves the certificate under
/etc/letsencrypt/live/example.com/. - Creates an HTTPS copy of your vhost, named
example.com-le-ssl.conf, with the certificate paths filled in.
Newer certbot versions add the HTTP to HTTPS redirect by default; older ones ask. If you are asked, choose to redirect.
Repeat for each site:
sudo certbot --apache -d example.net -d www.example.netUse a separate certificate per site, as above. A single certificate for many unrelated domains means one failing domain blocks renewal for all of them.
Tip: Add --dry-run to any certbot certonly or renew command to test against the staging server first. It does not count against Let's Encrypt rate limits.
What certbot changed
Look at the new file, for example /etc/apache2/sites-available/example.com-le-ssl.conf on Ubuntu or /etc/httpd/conf.d/example.com-le-ssl.conf on AlmaLinux. It contains a <VirtualHost *:443> block with lines such as:
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.confYour original port 80 vhost gets rewrite lines that redirect to HTTPS. If you later edit the site config, edit both the port 80 and the -le-ssl file so they stay in step.
Never edit files in /etc/letsencrypt/live/. They are links to the current certificate and change on every renewal.
Test automatic renewal
Certbot installs a systemd timer (or, for the snap, its own timer) that runs twice a day and renews certificates within 30 days of expiry. Check the timer:
systemctl list-timers | grep certbotThen simulate a renewal of every certificate:
sudo certbot renew --dry-runIt should end with Congratulations, all simulated renewals succeeded. The Apache plugin reloads Apache after a real renewal, so the new certificate is used without any action from you.
Check it works
List what certbot manages:
sudo certbot certificatesYou should see each certificate with its domains and expiry date. Then test the live site:
curl -I https://example.com
curl -I http://example.comThe first should return 200 OK. The second should return 301 Moved Permanently with Location: https://example.com/. In a browser, the padlock should show a certificate issued by Let's Encrypt.
Troubleshooting
Could not find a VirtualHostor certbot asks which file to use: no vhost has a matchingServerNameorServerAlias. Add the domain to the vhost, reload Apache and try again.Timeout during connectorConnection refused: Let's Encrypt cannot reach port 80. Open HTTP in UFW or firewalld and make sure DNS points at this server, not an old host.too many certificates already issued: you hit a rate limit after repeated attempts. Wait, and use--dry-runwhile you fix the cause. See Let's Encrypt too many requests.- Browser shows "Not secure" on some pages: the certificate is fine but the page loads images or scripts over
http://. Update those links to HTTPS. - Renewal dry run fails after you changed the redirect: your own rules may block
/.well-known/acme-challenge/. Exclude that path from any redirect or password protection.
Next steps
- Fine-tune redirects for www and non-www: Apache redirects with.htaccess.
- Add HSTS and other headers: Harden Apache security headers.
