How to Install Let's Encrypt on Apache Ubuntu: Free HTTPS Guide

Learn how to install Let's Encrypt on Apache Ubuntu with Certbot, configure free HTTPS, automatic renewal and 301 redirect in production.

HTTPS is no longer optional. Browsers mark HTTP sites as “Not secure”, external APIs reject webhooks without TLS, and SEO penalizes domains without a certificate. Let’s Encrypt solves this for free — valid X.509 certificates, trusted by every modern browser, issued by a public certificate authority.

This tutorial covers installing Let’s Encrypt on an Apache server running Ubuntu 22.04 or 24.04, using the official Certbot client. The focus is production configuration: issuance, HTTP to HTTPS 301 redirect, automatic renewal and real certificate validation. Estimated execution time: 15 to 20 minutes, assuming DNS is already pointing to the server.

The target persona is a sysadmin or developer who already has Apache configured serving a working site over HTTP and needs to add TLS. If you are still provisioning the server from scratch, install Apache first (sudo apt install apache2) and come back here once curl http://your-domain returns HTML.

Prerequisites

Before starting, confirm the server meets the prerequisites. Let’s Encrypt HTTP-01 validation needs to reach the server over port 80, so any firewall block or misaligned DNS makes issuance fail on the first certbot run.

Prerequisites

Ubuntu 22.04 LTS or 24.04 LTS with sudo access. Apache 2.4+ already installed and serving the site over HTTP. Public domain (e.g., example.com) with an A record pointing to the server IP. Ports 80 and 443 open in the firewall and in any provider security group. Valid email for expiration notifications.

ACME Client Certbot 2.x
Plugin python3-certbot-apache
Validation port 80 (HTTP-01)
Cert validity 90 days

Check DNS before continuing. Run dig +short your-domain.com and confirm the returned IP matches the server. If not, propagation may still be in progress (typical TTL of 1 to 24 hours) — wait before attempting to issue.

Installing Certbot and the Apache plugin

Certbot is available as an APT package on Ubuntu, but the version in the official repositories tends to lag behind. For production, the method recommended by the project itself is snap, which keeps the client updated automatically. This section covers snap installation, which is the most robust.

01

Update the package index and remove any old Certbot version that may have come from APT:

sudo apt update
sudo apt remove certbot python3-certbot-apache -y

If you never installed it before, remove simply does nothing — it is safe to run.

02

Install snap core (it ships by default on Ubuntu 22.04+, but this ensures the latest version):

sudo snap install core
sudo snap refresh core
03

Install Certbot via snap and create the symlink so the binary is available in PATH:

sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot

The --classic mode is required because Certbot needs to write to directories outside the snap sandbox (/etc/letsencrypt, /etc/apache2, /var/log/letsencrypt).

04

Confirm the installation:

certbot --version

The output should be something like certbot 2.11.0. If “command not found” appears, check that /snap/bin is in PATH or redo the symlink.

Verifying the Apache configuration

The Certbot --apache plugin reads VirtualHost files in /etc/apache2/sites-enabled/ to discover which domains are served and which vhost to edit. For the plugin to work, each vhost needs the ServerName directive (and optionally ServerAlias) correctly configured.

05

List active vhosts:

sudo apachectl -S

Look for entries with port 80 namevhost listing your domain. If the server responds with *:80 is a NameVirtualHost but ServerName is empty, edit the vhost before continuing.

06

Open your site’s vhost (usually /etc/apache2/sites-available/your-domain.conf or 000-default.conf) and confirm it has ServerName:

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

If you edited it, reload Apache:

sudo systemctl reload apache2
Watch the firewall

Ubuntu ships with UFW disabled by default, but if you enabled it, allow HTTPS before issuance:

sudo ufw allow 'Apache Full'
sudo ufw status

The Apache Full rule allows 80 and 443. If you only allow Apache (without Full), port 443 stays blocked and the site does not even load after configuration.

Issuing the certificate

With Certbot installed and Apache configured, issuance is a single command. The --apache plugin detects the domains, talks to Let’s Encrypt via HTTP-01, receives the certificate, edits the VirtualHosts to include the SSL directives and (optionally) configures the 301 redirect.

07

Run Certbot in interactive mode:

sudo certbot --apache

You will be asked:

  • Email for notifications (recovery + upcoming expiration)
  • Terms of Service acceptance (A to accept)
  • Opt-in for the EFF newsletter (N if you prefer not to receive)
  • Which domains to certify (select all if you want HTTPS on all)
  • Whether to automatically redirect HTTP to HTTPS (choose 2 to force redirect — recommended)
08

If everything went well, the output ends with something like:

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
This certificate expires on 2026-08-27.

Certbot also creates a new vhost file with the -le-ssl.conf suffix in /etc/apache2/sites-available/ containing the <VirtualHost *:443> block with the SSL directives.

Let's Encrypt rate limit

Let’s Encrypt limits 5 failed issuances per hour from the same IP and 50 certificates per registered domain per week. If you are testing, use the --staging flag (sudo certbot --apache --staging) — it issues against the test environment, the certificate is not trusted by browsers but does not consume the production rate limit.

Configuring automatic renewal

Snap installation already comes with automatic renewal configured via systemd timer. You do not need to edit crontab or create scripts — snap keeps the timer running twice a day, and Certbot only makes the real API call when the certificate is within 30 days of expiration.

09

Check that the timer is active:

sudo systemctl status snap.certbot.renew.timer

The output should show Active: active (waiting) and the next scheduled execution.

10

Run a renewal dry-run to confirm everything works:

sudo certbot renew --dry-run

The dry-run simulates a renewal against the Let’s Encrypt staging environment — it does not consume rate limit, does not replace the real certificate. If the output ends with Congratulations, all simulated renewals succeeded, you are good.

Post-renewal hook

If you run Apache behind a reverse proxy (HAProxy, for example) that keeps the certificate at another path, add a deploy hook in /etc/letsencrypt/renewal-hooks/deploy/:

#!/bin/bash
cp /etc/letsencrypt/live/example.com/fullchain.pem /etc/haproxy/cert.pem
systemctl reload haproxy

Mark it executable (chmod +x). Certbot runs every script in this directory after each successful renewal.

Verifying the certificate in production

After issuance, validate the certificate from the outside — it is not enough for Certbot to say it worked, you need to confirm the browser sees the site as secure and that the certificate chain is complete.

Run on your local terminal (not on the server):

curl -I https://example.com

The response should start with HTTP/2 200 or HTTP/1.1 200 OK. If a certificate error appears, something is wrong with the chain.

To inspect the certificate in detail:

echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates -issuer -subject

The output shows issuer (Let's Encrypt), subject (your domain) and validity dates. Confirm that the notAfter date is ~90 days ahead.

For a complete SSL/TLS configuration test, use SSL Labs at https://www.ssllabs.com/ssltest/analyze.html?d=example.com. A default Certbot 2.x configuration usually scores an A.

Troubleshooting common issues

Error “Failed authorization procedure”

This means Let’s Encrypt could not reach http://your-domain/.well-known/acme-challenge/.... Common causes: port 80 blocked in the firewall, DNS pointing to the wrong IP, or Apache not listening on 0.0.0.0:80. Check in this order:

sudo ufw status
sudo ss -tlnp | grep :80
dig +short your-domain.com

Error “Could not find a usable ‘apache2ctl’ binary”

Certbot looks for apache2ctl in PATH. On some custom systems, the binary is in /usr/sbin/ but the snap PATH does not include it. Create a symlink:

sudo ln -s /usr/sbin/apache2ctl /usr/local/bin/apache2ctl

Redirect loop after installation

Usually Apache behind a reverse proxy (CloudFront, Cloudflare) that terminates TLS before the server. Apache receives HTTP, redirects to HTTPS, the proxy sends HTTP again, loop. Solution: configure HTTPS=on via a trusted header in the vhost or disable the Apache redirect (let the proxy handle it).

Next steps

With HTTPS working, it is worth hardening the TLS configuration:

  • Adjust SSLProtocol to disable TLS 1.0 and 1.1 in the SSL vhost (SSLProtocol -all +TLSv1.2 +TLSv1.3)
  • Add the HSTS header after validating that everything is running well: Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
  • Configure OCSP stapling with SSLUseStapling on to reduce validation latency
  • Consider moving to HTTP/2 (Protocols h2 http/1.1) — requires mod_http2 enabled

If you are putting this into production and want infrastructure without worrying about firewall, kernel updates or Apache tuning, a Hostini VPS ships with Ubuntu LTS, snap installed and ports 80/443 already open — just point DNS and run certbot --apache.

Frequently asked questions

How long does a Let's Encrypt certificate last and how does renewal work?

Let's Encrypt certificates are valid for 90 days. Certbot installs a systemd timer (`certbot.timer`) that runs `certbot renew` twice a day. Renewal only hits the API when the certificate is within 30 days of expiring, so running frequently does not consume rate limit.

Can I use Let's Encrypt on a domain without public DNS pointing to it?

Not for HTTP-01 validation (which Certbot uses by default with `--apache`). Let's Encrypt needs to reach `http://your-domain/.well-known/acme-challenge/...` during issuance. For internal domains, use the DNS-01 challenge with the appropriate `certbot-dns-*` plugin for your provider.

How do I install a wildcard certificate (*.example.com) with Apache?

Wildcards require the DNS-01 challenge, so the `--apache` plugin does not work. Use `certbot certonly --manual --preferred-challenges=dns -d '*.example.com' -d example.com` or a specific DNS plugin (`certbot-dns-cloudflare`, etc) and manually configure `SSLCertificateFile` and `SSLCertificateKeyFile` in the VirtualHosts.

Why does Certbot fail with 'Connection refused' during validation?

Usually firewall blocking port 80, Apache not listening on 0.0.0.0:80, or DNS not resolving to the server IP. Check `sudo ufw status`, `sudo ss -tlnp | grep :80` and `dig +short YOUR-DOMAIN` before retrying.

How do I force HTTPS without breaking the ACME renewal challenge?

Certbot automatically adds a `RewriteCond %{REQUEST_URI} !^/.well-known/acme-challenge/` exception in the redirect when you pick `2 - Redirect` during `certbot --apache`. If you edit the vhost manually, keep that exception — without it, HTTP-01 renewal fails.

What is the difference between `certbot --apache` and `certbot certonly --webroot`?

`--apache` uses the plugin that edits vhosts automatically, installs the certificate and configures redirect. `certonly --webroot` only issues the certificate to a directory, leaving you to configure Apache manually — useful when you have a custom configuration template or manage many sites via Ansible.

Topics:
Next steps Ryzen cloud with NVMe storage and always-on DDoS protection.Go live on a Hostini VPS →
Was this tutorial helpful?
Chat on WhatsApp