Websites & apps

Secure Apache with a free Let's Encrypt certificate

Install certbot with its Apache plugin, issue free Let's Encrypt certificates for each virtual host, redirect HTTP to HTTPS and confirm that automatic renewal works.

BeginnerLinux server4 min read

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 ServerName and ServerAlias set. See Set up Apache virtual hosts.
  • DNS A records for the domain and www pointing at your server, for example 203.0.113.10. Check with dig +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_ssl installed (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:

Bash
sudo apt update
sudo apt install -y certbot python3-certbot-apache

AlmaLinux and Rocky Linux

Certbot comes from the EPEL repository:

Bash
sudo dnf install -y epel-release
sudo dnf install -y certbot python3-certbot-apache

The certbot project also publishes a snap package that tracks the newest release. Either route works; do not install both. Check what you have with:

Bash
certbot --version

Issue a certificate for each site

Run certbot with the Apache plugin and name every host the certificate should cover:

Bash
sudo certbot --apache -d example.com -d www.example.com

The first time, certbot asks for an email address for expiry warnings and for you to accept the Let's Encrypt terms. It then:

  1. Finds the vhost whose ServerName or ServerAlias matches.
  2. Places a temporary validation file and asks Let's Encrypt to fetch it over port 80.
  3. Saves the certificate under /etc/letsencrypt/live/example.com/.
  4. 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:

Bash
sudo certbot --apache -d example.net -d www.example.net

Use 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:

Apache
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf

Your 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:

Bash
systemctl list-timers | grep certbot

Then simulate a renewal of every certificate:

Bash
sudo certbot renew --dry-run

It 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:

Bash
sudo certbot certificates

You should see each certificate with its domains and expiry date. Then test the live site:

Bash
curl -I https://example.com
curl -I http://example.com

The 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 VirtualHost or certbot asks which file to use: no vhost has a matching ServerName or ServerAlias. Add the domain to the vhost, reload Apache and try again.
  • Timeout during connect or Connection 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-run while 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

Was this guide helpful?

Still stuck?

Ask MdmHost AI for a quick answer, or open a ticket and our team will take a look. We are here 24/7.