bg-tutorials

How to Install an ACME SSL Certificate on Apache and NGINX

Certbot is the reference ACME client on Linux, and it does more than fetch a certificate: its Apache and NGINX plugins edit the server configuration, set up the HTTPS redirect and reload the service, then keep the certificate renewed on a timer. It works with a commercial certificate authority as well as a free one, provided you pass the External Account Binding credentials your CA issues.

That shrinking is the reason to automate. Under CA/Browser Forum ballot SC-081v3, publicly trusted TLS certificates have been capped at 200 days since March 15, 2026, dropping to 100 days on March 15, 2027 and to 47 days on March 15, 2029. No publicly trusted authority issues a one-year TLS certificate any more, commercial or otherwise, so any guide that still describes annual renewals is describing the world before that ballot took effect.

Before you begin

  • Root or sudo access to the server.
  • A domain name resolving to the server’s public IP address. Certbot validates control of the name from the outside, so an internal-only name cannot be used.
  • Port 80 open for HTTP validation, and port 443 open so the site can actually serve HTTPS afterwards.
  • Apache or NGINX already running and serving the site over HTTP. The plugins edit an existing configuration rather than creating one.
  • For a commercial CA, the ACME directory URL, an EAB Key ID and an EAB HMAC Key from your account portal.

Step 1: Install Certbot

There are three routes. The project’s preferred one is snap, because the snap always carries the current release, and version matters here: the renewal behaviour described later changed in Certbot 4.0, so a distribution package from before that behaves differently.

Snap, on any systemd distribution

Install snapd first if it is not present. On Debian and Ubuntu:

sudo apt install snapd

On AlmaLinux and Rocky Linux:

sudo dnf install snapd

Then install Certbot and put it on the path:

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

That last line is a symlink into /usr/bin/, so it will collide if a distribution package already owns that path. Check which one you actually got afterwards, because having two installations and running the older one silently is a genuinely confusing state to debug:

certbot --version

Distribution packages

Simpler, at the cost of running whatever version your distribution ships. On Ubuntu and Debian, install Certbot with the plugin for your web server. For Apache:

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

For NGINX:

sudo apt update
sudo apt install certbot python3-certbot-nginx

On AlmaLinux or Rocky Linux, enable EPEL first, then install the same packages:

sudo dnf install epel-release
sudo dnf update
sudo dnf install certbot python3-certbot-apache

Substitute python3-certbot-nginx for the NGINX plugin. Install only the plugin you need; there is no benefit to having both.

pip, for constrained environments only

python3 -m pip install --upgrade certbot

Certbot’s own documentation is blunt about this route: installing through pip is supported only on a best-effort basis and only inside a virtual environment. It gives you the core tool and nothing else, so no web server plugin and no automatic renewal, both of which you would then configure yourself. Use it for containers or unusual environments, not for a normal server.

Step 2: Request and install the certificate

Option A: Let Certbot do everything

sudo certbot --apache
sudo certbot --nginx

Certbot reads the server configuration, lists the names it found, and asks which to secure, whether to redirect HTTP to HTTPS, and for a contact address. It then validates, writes the certificate paths into the configuration and reloads the service. This route uses Certbot’s default authority, which is Let’s Encrypt.

Option B: A commercial CA with EAB

sudo certbot --nginx \
  --non-interactive \
  --agree-tos \
  --email [email protected] \
  --server https://acme.yourca.example/v2/DV \
  --eab-kid YOUR_EAB_KEY_ID \
  --eab-hmac-key YOUR_EAB_HMAC_KEY \
  --domain example.com \
  --domain www.example.com \
  --cert-name my-website-cert

Swap –nginx for –apache as appropriate. Points worth noting:

  • –server is what sends the request to your CA instead of the default. Omit it and you get a Let’s Encrypt certificate no matter what the EAB values say.
  • –eab-hmac-key must be pasted exactly as the CA supplied it, since it is already base64url encoded. Re-encoding it, or picking up a trailing space, produces a rejection that looks like wrong credentials.
  • –cert-name fixes the name of the certificate lineage, which is what you will refer to later in renewal commands. Without it Certbot names the lineage after the first domain.
  • –non-interactive means nothing will pause for confirmation, so check the domain list before running it.

Option C: Issue only, configure the server yourself

When you do not want Certbot editing your configuration, use the certonly subcommand with webroot validation. Note that certonly is a subcommand rather than an option, so it takes no leading dashes:

sudo certbot certonly --webroot \
  -w /var/www/html \
  -d example.com \
  -d www.example.com \
  --server https://acme.yourca.example/v2/DV \
  --eab-kid YOUR_EAB_KEY_ID \
  --eab-hmac-key YOUR_EAB_HMAC_KEY \
  --email [email protected] \
  --agree-tos \
  --non-interactive

-w is the document root the challenge file is written into, and it has to be the directory the site actually serves over port 80. Drop the –server and EAB lines if you are using Let’s Encrypt.

The certificate and key are written to /etc/letsencrypt/live/example.com/. That path is used whichever authority issued the certificate, which surprises people using a commercial CA; the directory is named after the software, not the issuer. You can move it with the –config-dir and –work-dir options, but there is rarely a reason to. Point your Apache or NGINX configuration at the files there and reload the service.

Step 3: Check that it worked

Confirm the service came back up. For Apache on Debian or Ubuntu:

sudo systemctl status apache2

For Apache on AlmaLinux or Rocky, the unit is named differently:

sudo systemctl status httpd

For NGINX on any of them:

sudo systemctl status nginx

Then read the certificate the server is actually presenting. Piping into openssl x509 prints the fields you care about instead of the whole handshake, and the redirect from /dev/null stops the command waiting for input:

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

Check the issuer is the authority you expected and the dates are new. Certbot can also list what it manages, which is the quickest way to see the lineage names and expiry dates together:

sudo certbot certificates

For a view from outside your network, including whether the intermediate certificate is being served alongside the leaf, use the SSL Checker.

Step 4: Renewal, and when it actually fires

Certbot installs a systemd timer or a cron job at install time, so there is nothing to schedule. Confirm it is there:

systemctl list-timers | grep certbot

It runs roughly twice a day and does nothing unless a certificate is due. What counts as due is the part worth updating your mental model on.

  • Since Certbot 4.0, a certificate renews when one third of its lifetime remains, or when half remains if the lifetime is under 10 days. This replaced a hardcoded 30 days before expiry. Because the trigger is now a proportion, it adjusts itself as lifetimes fall to 100 and then 47 days, with nothing for you to change.
  • Certbot also checks ACME Renewal Information where the certificate authority publishes it, and will renew early if the CA asks it to. An early ARI response overrides a later locally calculated time. ARI is deliberately skipped during a dry run, because a dry run talks to the staging environment.
  • The renew_before_expiry field in the renewal configuration still overrides the default if you need a specific schedule.

Test the whole path safely. Unlike some clients, Certbot has a real dry run that exercises validation and renewal against the staging environment without issuing anything or consuming rate limits:

sudo certbot renew --dry-run

Run that after any change to the web server configuration, since a rewrite rule or a moved document root will break renewal months later, long after you have forgotten touching it.

Common problems

  • Validation fails with a 404 or a timeout. Confirm port 80 reaches the server from the internet and that the name resolves to this machine. A redirect from HTTP to HTTPS is fine, since the CA follows redirects, but a firewall or cloud security group closing port 80 is not.
  • The plugin cannot find your site. The Apache and NGINX plugins parse the existing configuration, so a virtual host with no matching ServerName, or an NGINX server_name that does not include the name you are requesting, leaves them nothing to match. Add the name to the configuration first.
  • Registration is refused although the EAB values look right. Check for whitespace, confirm you have not re-encoded the HMAC key, and make sure –server points at the endpoint the credentials were issued against.
  • The certificate went to the wrong CA. You omitted –server. Certbot defaults to Let’s Encrypt, and the EAB arguments alone do not redirect it.
  • Renewal stopped working after a configuration change. Run the dry run above; it reproduces the failure without waiting for the real renewal.
  • You are not sure which Certbot is running. Check certbot –version, particularly if you installed both a distribution package and the snap.

For error strings that come from the ACME protocol rather than from Certbot, such as badNonce, unauthorized or a CAA record refusing issuance, see our guide to fixing ACME SSL certificate errors.

Frequently Asked Questions

How long are ACME certificates valid?

At most 200 days, for every publicly trusted TLS certificate since March 15, 2026, whether it came from a free authority or a commercial one. The cap falls to 100 days in March 2027 and 47 days in March 2029. Let’s Encrypt has issued 90-day certificates throughout and also offers much shorter ones; commercial authorities that previously sold one-year certificates now issue within the same ceiling as everyone else.

Why are my certificates in /etc/letsencrypt if I am not using Let’s Encrypt?

Because that path is Certbot’s own storage location, named after the project’s origins rather than after the issuing authority. A certificate from any ACME CA lands in /etc/letsencrypt/live/ under its lineage name. It can be relocated with –config-dir and –work-dir, though doing so means passing those options on every subsequent command, including renewals.

When exactly does Certbot renew?

When one third of the certificate’s lifetime remains, or half if the lifetime is under 10 days. That has been the rule since Certbot 4.0; before that it was a fixed 30 days before expiry, which is what older guides describe. Certbot may also renew earlier if the CA publishes renewal information asking it to, and the renew_before_expiry setting overrides the default if you need something specific.

Do I need EAB credentials?

Only if your certificate authority requires them, which commercial ones generally do and Let’s Encrypt does not. They tie the ACME account to the account you already hold with the CA. If you are using Let’s Encrypt, leave out –eab-kid, –eab-hmac-key and –server entirely.

Should I install Certbot from snap or from my distribution?

Snap is the project’s preferred route and always carries the current release, which matters because renewal behaviour changed in Certbot 4.0. A distribution package is simpler and perfectly workable if it is recent. What to avoid is having both, since the symlink the snap creates competes with the package for /usr/bin/certbot; check certbot –version if you are unsure which one you are running.

Can I get a wildcard certificate this way?

Not with the HTTP validation shown here. Wildcards require a DNS-01 challenge, because no certificate authority validates a wildcard over HTTP. Certbot supports DNS validation through provider plugins, which you install alongside it and configure with an API credential for your DNS host.

Will Certbot overwrite my web server configuration?

The –apache and –nginx plugins do edit it, which is the point of using them: they add the certificate paths and, if you agree, the HTTP to HTTPS redirect. They keep backups and are generally reliable on a conventional configuration. If yours is unusual, or managed by configuration management that would revert the change, use certonly instead and reference the files yourself.

For more on the protocol behind all of this, see what the ACME protocol is and our ACME SSL certificates page. To install a certificate by hand instead, see our guides for Apache and NGINX.

Save 10% on SSL Certificates when ordering from SSL Dragon today!

Fast issuance, strong encryption, 99.99% browser trust, dedicated support, and 25-day money-back guarantee. Coupon code: SAVE10

A detailed image of a dragon in flight
Written by

I've been writing for SSL Dragon for over 10 years, focusing entirely on SSL certificates and digital security. My job is to take complex cybersecurity topics and strip away the jargon, making sure you get the clear, practical information you need to keep your website safe.