SSD Nodes Learn Hosting plans →
How to do am Matt ConnorBy Matt Connor · Updated 2026-08-07

Install Certbot for Apache on Ubuntu 24.04

Get free HTTPS for Apache on Ubuntu 24.04 with one command. Skip snap: apt ships Certbot 2.9.0, but a missing ServerName fit block issuance.

Wetin you dey build

Na Apache site for Ubuntu 24.04 wey dey answer for HTTPS with free Let's Encrypt certificate wey browser trust. Certbot issue am, and systemd timer go renew am automatically, so you no need think about am again. The command wey do the work na one line. Anything wey fit go wrong, go wrong before that line: vhost wey no get ServerName, port 80 wey provider firewall block, or DNS wey still point to the old server. Na why this guide spend most of the time for the conditions wey must dey ready, and e show the exact error string each mistake print.

Two things dey outside this scope. If your web server na nginx, the process follow the same general shape, but the plugin and configs different. Use the nginx version of this guide instead. If the thing wey you dey secure na internal-only service, like admin panel for private address or staging box wey nobody else dey visit, you no need certificate authority at all. A self-signed certificate need less setup and e work offline.

Prerequisites, and the three ways this fit fail before Certbot even run

  • Apache don already dey serve your site over plain HTTP. Certbot Apache plugin dey edit site wey already exist; e no dey create one. If na bare VPS you dey start with, first build the LAMP stack for Ubuntu 24.04 then come back. This guide na the TLS chapter wey dey miss.
  • Public domain wey get A record for your VPS address. Let's Encrypt HTTP-01 challenge mean say their validation servers go connect to your box from internet: no NATed homelab without port forward, no .local names, no bare IPs. dig +short example.com must return your VPS address. If you change DNS within the last hour, wait for the old record TTL to expire before you issue.
  • If AAAA record dey, e must correct. Let's Encrypt dey prefer IPv6 when AAAA record dey published. So stale AAAA fit make validation fail even when curl from your laptop, wey probably dey use IPv4, dey work fine. Publish correct AAAA or no publish any one.

Ports 80 and 443 need to dey open for ufw and your provider network firewall. Most hosting panels get second firewall wey the OS no dey see. HTTP-01 validates specifically through port 80; you no fit run this for 443 only.

sudo ufw allow "Apache Full"
sudo ufw status

Once these things dey ready, the whole work fit take fifteen minutes, and ten of those minutes na reading.

Snap or apt Certbot? For 24.04, apt don dey okay at last

Certbot move go snap distribution years ago for good reason: distro packages turn old and stop moving. Ubuntu 20.04 ship Certbot 0.40 and never update am, and the project tire to debug bugs wey don old reach five years. For 24.04, that reason don go. The archive dey ship Certbot 2.9.0, wey be current-generation release, and unattended-upgrades dey keep am patched. My recommendation for this OS: use apt. You go skip the snapd daemon, the Apache plugin go install for the same transaction, and the renewal timer go integrate with systemd the normal Debian way.

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

Correct result na: certbot 2.9.0. The python3-certbot-apache package na the plugin wey dey read and edit your Apache configs. Without am, certbot --apache go fail with The requested apache plugin does not appear to be installed.

Snap still make sense for two cases: you want the newest Certbot the same day e release, or you need a DNS plugin wey dem distribute only as snap. Several of the certbot-dns-* provider plugins dey like that. If you choose that option:

sudo apt remove -y certbot python3-certbot-apache
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot

Any one wey you choose, never run both. Two installations mean two renewal schedulers dey fight over /etc/letsencrypt, and the certbot wey your shell find for PATH fit no be the one wey own your certificates. The apt remove line above no be optional decoration.

The vhost wey Certbot go edit must don dey, ServerName na the main thing

certbot --apache dey work by finding the port-80 virtual host wey ServerName or ServerAlias match each -d domain wey you pass. E use am prove say you control the domain, then e write SSL twin of that vhost. If no ServerName match, no match go happen, and Ubuntu default 000-default.conf get ServerName commented out. Na that one commented line dey cause this guide one big command to fail most times.

So before you touch Certbot, give the site correct name-based vhost. Create /etc/apache2/sites-available/example.com.conf:

<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /var/www/example.com
    ErrorLog ${APACHE_LOG_DIR}/example.com-error.log
    CustomLog ${APACHE_LOG_DIR}/example.com-access.log combined
</VirtualHost>

Enable am and confirm say Apache fit parse am and route the name go am:

sudo a2ensite example.com.conf
sudo apache2ctl configtest
sudo systemctl reload apache2
sudo apache2ctl -S

configtest suppose print Syntax OK. If e also print AH00558: apache2: Could not reliably determine the server's fully qualified domain name, na warning about the global ServerName, no be your vhost. E no cause problem here, and you fit silence am with echo "ServerName $(hostname -f)" | sudo tee /etc/apache2/conf-available/servername.conf && sudo a2enconf servername && sudo systemctl reload apache2.

The -S output na the check wey matter. You want line like port 80 namevhost example.com (/etc/apache2/sites-enabled/example.com.conf:1) with alias www.example.com under am. Apache dey report the sites-enabled symlink wey e actually read, no be the file wey you edit inside sites-available. If example.com no dey listed against port 80, Certbot no go find am too.

Emit di certificate: certbot --apache

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

First time you run am, e go ask three things: email address (dem go use am for your ACME account and urgent CA notices; Let's Encrypt no dey send expiry warnings again, so na you go monitor renewals), whether you agree to the Let's Encrypt terms, and whether you want share your email with EFF. E no dey ask redirect question again: since Certbot 2.0, Apache installer dey redirect HTTP go HTTPS by default, and na wetin you want. Pass --no-redirect only if you truly need plain HTTP to continue serving content.

Success go look like this, and you suppose read am instead of just skim am:

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-10-14.

Deploying certificate
Successfully deployed certificate for example.com to /etc/apache2/sites-available/example.com-le-ssl.conf
Successfully deployed certificate for www.example.com to /etc/apache2/sites-available/example.com-le-ssl.conf
Congratulations! You have successfully enabled HTTPS on https://example.com and https://www.example.com

Behind that message, Certbot don do four things: e enable Apache ssl module if e never dey enabled, e write example.com-le-ssl.conf, wey be copy of your vhost for *:443 with SSLEngine on and the certificate paths, e enable am, and e add RewriteRule block to the original port-80 vhost wey 301 everything go HTTPS. E edit your original vhost file; e no replace am. The SSL twin dey beside am, so you fit read every line wey e add.

Where certificate dey really live, and why you no ever copy am

Everything dey under /etc/letsencrypt/live/example.com/: fullchain.pem (the certificate plus intermediate chain, wetin servers suppose point to), privkey.pem (the private key, only root fit read am), plus cert.pem and chain.pem for software wey need the parts separately. These na symlinks into /etc/letsencrypt/archive/, and na this indirection dey handle renewal: renewal dey write new files into archive/ and point the symlinks to dem again. Point any other software to the live/ paths and e go pick up renewals by itself; copy the files go another place and you don create outage wey go happen after 90 days.

The other file wey you suppose know na /etc/letsencrypt/renewal/example.com.conf. E record how dem issue this certificate, authenticator = apache, installer = apache, and the domains, so renewal fit repeat the process without anybody attending to am, including reloading Apache afterward.

Renewal don already schedule, verify am; no need build another one

Let's Encrypt certificates dey last 90 days by design, and the apt package don already install the machinery: a systemd timer wey dey run Certbot two times every day for random times, to renew any certificate wey remain within 30 days before e expire. No add cron job on top; second scheduler no add anything apart from log noise and rate-limit exposure.

systemctl list-timers certbot.timer
sudo certbot renew --dry-run

The first command dey show say the timer active, with one NEXT time sometime within the next 24 hours. The schedule na two times every day with randomized delay, so the exact time dey deliberately unpredictable (for snap install, the timer na snap.certbot.renew.timer instead). The dry run dey perform complete renewal rehearsal against Let's Encrypt's staging environment, with real challenge, no certificate issuance, and no rate-limit cost. Correct result go end with:

Congratulations, all simulated renewals succeeded:
  /etc/letsencrypt/live/example.com/fullchain.pem (success)

If the dry run fail, the real renewal for about 60 days go fail the same way. Fix am now, while the current certificate still get its full validity period ahead. The usual cause na firewall rule wey dem add after issuance, wey close port 80 again.

Verify am with curl, and wetin the padlock suppose show

curl -sI http://example.com | head -n 3
curl -I https://example.com
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -issuer -dates

The first one suppose return HTTP/1.1 301 Moved Permanently with a Location: https://example.com/ header. Na the redirect wey Certbot install. The second one suppose return HTTP/1.1 200 OK, and curl no suppose report any TLS complaint. The third one go print the issuer, an O = Let's Encrypt line with short CN like R12 or E7, and notAfter wey dey roughly 90 days from now. For browser, you go see the padlock. If you click am, e go show the same issuer. If curl dey work but browser dey warn, you almost certainly dey look at cached page or wrong hostname, no be certificate problem.

Multiple sites: one SAN certificate or one cert per site

Both options dey work, and dem renew the same way. For unrelated sites wey dey on the same server, run the issue command once for each site. Each site go get its own directory under live/ and its own renewal config. If one domain get problem, e no go block renewal for the others. Na this I normally recommend.

For one site wey get different names, put all of dem for one SAN certificate. One certificate fit carry up to 100 names. You don already do this above with example.com and www.example.com. If you wan add another name to an existing certificate later, reissue am with the certificate name and the complete new list:

sudo certbot --apache --cert-name example.com -d example.com -d www.example.com -d blog.example.com

Certbot go notice say the domain set don change, ask you to confirm the expansion, then replace the certificate in place. E go still use the same live/ path, so you no need change anything else. Remember say this list replaces the old one; e no dey append. If you leave out www from that command, the new certificate go remove am without warning.

Wildcards need DNS-01, and most times you no need wildcard

HTTP-01 no fit issue *.example.com, because putting file for web server only proves say you control one hostname, not the whole namespace. Wildcards need DNS-01 challenge: Certbot go set TXT record for _acme-challenge.example.com. For this to work, you normally need a certbot-dns-* plugin with API credentials for your DNS provider. Or you fit edit TXT records by hand every time renewal happen with --manual, but this one dey stressful, so no plan around am. The complete guide, from how TXT record work to plugin wey renew certificate without manual work, dey for wildcard certificates with Certbot over DNS-01. Straight advice: if you get four known subdomains, SAN certificate wey list all four dey simpler than wildcard, and you no need keep DNS API keys for the server.

Failure modes, with the strings wey you go see

Certbot no gree start because Apache config spoil.

The apache plugin is not working; there may be problems with your existing configuration.
The error was: MisconfigurationError('Error while running apache2ctl configtest.\n\nAction \'configtest\' failed.\nThe Apache error log may have more information.\n\nAH00526: Syntax error on line 12 of /etc/apache2/sites-enabled/example.com.conf')

The plugin dey run configtest before e touch anything, and e stop if Apache itself get problem. The \ns dey literal because na Certbot dey print the exception's repr. Run sudo apache2ctl configtest by yourself: e go show the file and line. Most times, na typo from hand-editing, a SSLCertificateFile wey dey point to path wey no dey again, or module wey dem reference but no enable. Fix am until e print Syntax OK, then run Certbot again.

No vhost match the domain.

Unable to find a virtual host listening on port 80 which is currently the only challenge port.

Na the missing-ServerName failure from earlier, and e happen when issue start. Certbot search every enabled port-80 vhost for ServerName/ServerAlias wey match your -d, but e no find anything. sudo apache2ctl -S show wetin Apache dey route actually. Add the ServerName line to the correct vhost, reload, then retry. Another similar problem na when validation reach the wrong vhost. The challenge response come back Invalid response ... 404 because another site catch the request. Same diagnosis, same tool: apache2ctl -S.

Validation time out.

Certbot failed to authenticate some domains (authenticator: apache).
...
Detail: ...: Timeout during connect (likely firewall problem)

Let's Encrypt no fit open TCP connection to port 80 for the address wey your DNS advertise. Based on how common dem be: your provider network firewall, wey separate from ufw and you configure am for hosting panel; a ufw ruleset wey allow only 443 or only SSH; DNS still dey point to previous server; or the stale-AAAA problem, where their servers try IPv6 but your server answer only on IPv4. Test from outside the VPS: curl -I http://example.com from your laptop go reproduce wetin their validator dey see.

You retry until you enter rate limit.

Error creating new order :: too many failed authorizations recently: see https://letsencrypt.org/docs/rate-limits/

Let's Encrypt allow 5 failed validations per hostname per account per hour. Since their 2025 rate-limit rework, na refilling bucket be this: e dey add back roughly one retry every 12 minutes. If you keep retrying against broken firewall, the limit go finish quickly. Waiting go work, but the real fix na to change how you dey work: after any failure, debug with staging environment until e succeed.

sudo certbot certonly --apache --dry-run -d example.com -d www.example.com

Pay attention to certonly: na only certonly and renew subcommands accept --dry-run. The plain certbot --apache --dry-run form no gree run at all, and e tell you --dry-run currently only works with the 'certonly' or 'renew' subcommands. The dry run validate against staging. Staging get its own generous limits and e no issue real certificates, so you fit fail there all afternoon. Run the real command again only after staging pass. The other limits — 50 certificates per registered domain per week and 5 duplicates of the same name set per week — go affect you only if script dey reissue certificates in a loop.

After HTTPS don come up, remember say certificate secure the transport, not the server. Port 22 still dey receive password guesses all day. Pairing this with Fail2ban for Ubuntu 24.04 na the natural next thirty minutes.

FAQ

I suppose install Certbot with snap or apt for Apache for Ubuntu 24.04?

Use apt. Ubuntu 24.04 release Certbot 2.9.0, wey current enough for everything wey dey this guide, e dey receive security patches through unattended-upgrades, and e no need snapd. Choose snap only if you need the newest release immediately or a DNS plugin wey dem distribute only as snap. If you switch, apt remove certbot python3-certbot-apache first so two renewal schedulers no go dey run together.

Why Certbot dey talk say "Unable to find a virtual host listening on port 80"?

Na because no enabled port-80 vhost get ServerName or ServerAlias wey match the domain wey you pass with -d. Ubuntu default vhost release with ServerName commented out. Run sudo apache2ctl -S, find or create the vhost wey suppose own the name, add ServerName example.com, reload Apache, then run Certbot again.

How I fit fix "Timeout during connect (likely firewall problem)"?

Let's Encrypt no fit reach port 80 for the address wey your DNS publish. Check your provider panel-level network firewall together with ufw. Confirm say dig +short example.com dey return this VPS, and delete or correct any stale AAAA record. Validation prefer IPv6 whenever one dey. Confirm the fix from outside the server with curl -I http://example.com, then rehearse with sudo certbot certonly --apache --dry-run -d example.com before real issuance.

Certbot dey renew certificates automatically for Ubuntu 24.04?

Yes. The apt package install certbot.timer, wey be systemd timer wey dey run two times every day and renew any certificate wey go expire within 30 days, then reload Apache afterward. The snap use snap.certbot.renew.timer for the same work. Verify with systemctl list-timers certbot.timer and rehearse with sudo certbot renew --dry-run. No add your own cron job on top.

How I fit get wildcard certificate with Certbot and Apache?

Wildcards need DNS-01 challenge. Certbot must place TXT record for _acme-challenge.example.com. This means say you need a certbot-dns-* plugin with API credentials for your DNS provider. The --manual alternative need you to edit TXT records by hand every time renewal happen. If na only small number of known subdomains you get, SAN certificate wey list dem explicitly go simpler and e go keep DNS API keys off the server.