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 dey serve your site over plain HTTP. Certbot Apache plugin dey edit site wey already exist; e no dey create one. If you dey start from bare VPS, 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 forwarding, and no
.localnames. HTTP-01 fit validate bare IP, but Let's Encrypt dey issue IP certificates only for opt-inshortlivedprofile (160-hour lifetimes), and apt Certbot 2.9.0 no fit request ACME profiles or IP addresses. So this guide need domain name.dig +short example.commust return your VPS address. If you change DNS within the last hour, wait for the old record TTL to expire before you issue certificate. - If AAAA record dey, e must correct. Let's Encrypt always try IPv6 first when AAAA record dey published. If that connection timeout, e go retry over IPv4. But no other error get that second chance: AAAA wey point to address wey dey refuse connections, or to live server wey no be this one, go fail validation even while
curlfrom your laptop, probably over IPv4, dey work fine. Publish correct AAAA or no publish am at all.
Port 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 validate specifically over port 80; you no fit run this with 443 only.
sudo ufw allow "Apache Full"
sudo ufw statusOnce these things dey ready, the whole work na fifteen minutes, and ten of those minutes na reading.
Snap or apt Certbot? For 24.04, apt finally dey okay
Certbot move go snap distribution years ago for good reason: distro packages don old too much. Ubuntu 20.04 ship Certbot 0.40 and e never move am again, while the project tire to debug bugs wey don reach five years old. For 24.04, that reason don disappear. The archive ship Certbot 2.9.0, na February 2024 release from the modern 2.x line, and unattended-upgrades dey install any security update wey Ubuntu publish for am. Upstream don move go 5.x series since then, but nothing for this guide need am. My recommendation for this OS na: use apt. You go avoid 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 --versionCorrect 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.
The snap still make sense for two cases: you want the newest Certbot on the same day wey e release, or you need 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/certbotWhich one you choose, never run both. Two installations mean two renewal schedulers dey fight over /etc/letsencrypt, and the certbot wey your shell find inside 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 exist; 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 prove say you control the domain through that vhost, then e write an SSL twin of the vhost. If no ServerName match, no match dey. Ubuntu default 000-default.conf come with ServerName commented out. That one commented line na the most common reason why this guide one big command dey fail.
So before you touch Certbot, give the site a proper 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 -Sconfigtest 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 echo "ServerName $(hostname -f)" | sudo tee /etc/apache2/conf-available/servername.conf && sudo a2enconf servername && sudo systemctl reload apache2 go silence am.
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 for sites-available. If example.com no dey listed against port 80, Certbot no go find am too.
Issue the certificate: certbot --apache
sudo certbot --apache -d example.com -d www.example.comFirst run go ask three things: an email address (Let's Encrypt no longer stores am with your ACME account and no longer sends expiry warnings, so na you go monitor renewals), agreement to the Let's Encrypt terms, and whether to share your email with the EFF. Redirect question no dey again: since Certbot 1.0, the Apache installer dey redirect HTTP go HTTPS by default (dem remove the question itself for 1.4), and na wetin you want. Pass --no-redirect 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 2027-01-09.
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.comBehind that message, Certbot don do four things: e enable Apache's ssl module if e never dey enabled, write example.com-le-ssl.conf, wey be copy of your vhost for *:443 with SSLEngine on and the certificate paths, enable am, and add RewriteRule block to the original port-80 vhost wey 301 everything go HTTPS. Certbot edit your original vhost file; e no replace am. The SSL twin dey beside am, so you fit read every line wey Certbot 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 build am
Let's Encrypt certificates dey last 90 days by design, and the apt package don already install the machinery: one systemd timer wey dey run Certbot two times every day for random times, to renew any certificate wey go expire within 30 days. No add cron job join am; second scheduler no add anything apart from log noise and rate-limit exposure. Let's Encrypt don announce say from February 10, 2027 new certificates go default to 64-day lifetimes. The 30-day window still dey work with dem, renewal go simply happen around day 34 instead of day 60, so nothing dey change for this server.
systemctl list-timers certbot.timer
sudo certbot renew --dry-runThe 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: real challenge, no certificate issuance, and no rate-limit cost. Correct result dey 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 somebody add after issuance and wey close port 80 again.
Verify 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 -datesThe first one suppose return HTTP/1.1 301 Moved Permanently with a Location: https://example.com/ header. Na the redirect be this wey Certbot install. The second one suppose return HTTP/1.1 200 OK, and curl no suppose show any TLS complaint. The third one go print the issuer, an O = Let's Encrypt line with short CN like YE1 or YE2 (ECDSA keys), or YR1 or YR2 (RSA keys), plus notAfter wey dey roughly 90 days ahead. Certificate wey issue before Let's Encrypt retire the older intermediates fit still show CN like R12 or E7 until the next renewal. This no be error. 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, almost certainly na cached page or wrong hostname you dey look at, 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.comCertbot 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, then e stop if Apache itself get problem. The \ns dey literal because 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 manual editing, SSLCertificateFile wey dey point to path wey no dey again, or module wey dem reference but dem never 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.This na the missing-ServerName failure wey we talk about earlier, and e happen when you dey issue the certificate. Certbot search every enabled port-80 vhost for ServerName/ServerAlias wey match your -d, but e find nothing. sudo apache2ctl -S go show wetin Apache actually dey route. Add the ServerName line to the correct vhost, reload, then retry. Another similar case na when validation reach the wrong vhost. The challenge response come back Invalid response ... 404 because another site catch the request. Diagnosis still be the same, and na the same tool you go use: 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. The likely causes, in order, na your provider network firewall, wey separate from ufw and configured for the hosting panel; a ufw ruleset wey allow only 443 or only SSH; or DNS still dey point to old server. Stale AAAA by itself normally no cause this problem, because when the IPv6 attempt time out, Let's Encrypt go retry over IPv4. AAAA problem usually show as Connection refused or Invalid response from the wrong server instead. Test from outside the VPS: curl -I http://example.com from your laptop go reproduce wetin their validator dey see.
You retry until rate limit catch you.
Error creating new order :: too many failed authorizations (5) for "example.com" in the last 1h0m0s, retry after ... UTC: see https://letsencrypt.org/docs/rate-limits/...Let's Encrypt allow 5 failed validations per hostname per account per hour. E dey work like bucket wey dey refill, and e dey restore 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 the process: after any failure, debug with staging environment until e succeed.
sudo certbot certonly --apache --dry-run -d example.com -d www.example.comPay attention to certonly: na only certonly and renew subcommands accept --dry-run. The plain certbot --apache --dry-run form no go run at all; e go tell you --dry-run currently only works with the 'certonly' or 'renew' subcommands. The dry run dey 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. You go meet the other limits — 50 certificates per registered domain per week and 5 duplicates of the same name set per week — only if script dey reissue certificates inside loop.
After HTTPS don come up, remember say certificate secure the transport, not the server. Port 22 still dey receive password guesses throughout the day. Pair this with Fail2ban for Ubuntu 24.04 as the natural next thirty minutes.
FAQ
I suppose make I install Certbot with snap or apt for Apache on Ubuntu 24.04?
Use apt. Ubuntu 24.04 dey ship Certbot 2.9.0, wey current enough for everything for this guide, dey receive any security updates wey Ubuntu publish through unattended-upgrades, and no need snapd. Choose snap only if you need the newest release immediately or a DNS plugin wey dem distribute exclusively 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 dey ship 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. Correct or delete any old AAAA record, because validation dey try IPv6 first and only fall back to IPv4 after timeout. 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 dey install certbot.timer, a systemd timer wey dey run twice every day and renew any certificate wey go expire within 30 days, then reload Apache. The snap dey 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 a 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 for every renewal. If na only small number of known subdomains you get, SAN certificate wey list dem clearly dey simpler and e keep DNS API keys off the server.