Skip to content
Website Security

How to Set Up Free SSL on Nginx With Certbot

In this article12 sections

To set up free SSL on Nginx, install Certbot, make sure your domain points at the server and ports 80 and 443 are open, then run sudo certbot --nginx -d example.com -d www.example.com. Certbot proves you control the domain, gets a certificate from Let’s Encrypt, edits your Nginx configuration to use it, and installs a timer that renews it automatically. The whole job takes about five minutes, and the certificate costs nothing.

This guide is for Ubuntu 22.04 and 24.04 with Nginx already serving your site over plain HTTP. It also covers the three reasons the command fails, how to confirm renewal will work before you need it, and what changes when Cloudflare sits in front of the server.

Before you start: three checks

Nearly every failed attempt comes from one of these, so check them first.

1. The domain points at this server. Compare the server’s address with what DNS answers:

curl -4 -s ifconfig.me; echo
dig +short A example.com
dig +short A www.example.com
dig +short AAAA example.com

The first three lines must show the same IPv4 address. The last one is the check people miss: if the domain has an AAAA (IPv6) record, Let’s Encrypt prefers it. An old IPv6 address left over from a previous host makes validation fail even though the site loads fine for you. Correct it or delete it.

2. Ports 80 and 443 are open.

sudo ufw allow 'Nginx Full'
sudo ufw status

Port 80 must stay open even after the site moves to HTTPS, because renewals use it.

3. Nginx knows the domain. Certbot finds the right configuration by its server_name line:

sudo grep -R "server_name" /etc/nginx/sites-enabled/

You should see server_name example.com www.example.com;. If it still says _ or localhost, edit the file, then run sudo nginx -t && sudo systemctl reload nginx.

Step 1: Install Certbot

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

Step 2: Get and install the certificate

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

List every name the site answers to, each with its own -d. Certbot asks for an email address for your Let’s Encrypt account and for agreement to its terms. When it finishes you will see:

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
Successfully deployed certificate for example.com to /etc/nginx/sites-enabled/example.com
Congratulations! You have successfully enabled HTTPS on https://example.com

Certbot has added the certificate lines to your server block and a redirect from HTTP to HTTPS. Open the site in a browser and check for the padlock.

Step 3: Confirm renewal works

Let’s Encrypt certificates last 90 days. Certbot renews them when 30 days remain, using a timer it installed. Test the whole process now, without changing anything:

sudo certbot renew --dry-run
systemctl list-timers | grep -i certbot

The dry run must end with Congratulations, all simulated renewals succeeded, and the second command must show a timer with a next run time. If both are true, you never have to think about this certificate again.

Tip. Run the dry run again after any change to the firewall, to Nginx, or to DNS. A renewal that silently stopped working is how sites end up with an expired certificate three months later.

Step 4: Check what you have

sudo certbot certificates

This lists each certificate, the names it covers and its expiry date. To see what visitors actually receive:

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

When it fails

Certbot says Cause Fix
DNS problem: NXDOMAIN One of the names has no DNS record Add the record, or leave that -d out
Timeout during connect (likely firewall problem) Port 80 is closed, here or at the provider’s firewall Open port 80 and try again
Invalid response ... 404 The request reached a different server, or a different Nginx block Check the A and AAAA records and the server_name
too many failed authorizations recently Five failures for the same name within an hour Fix the cause, wait an hour, then retry
Could not automatically find a matching server block No server_name matches Add the name to the Nginx configuration

While you are still working out a problem, test with Let’s Encrypt’s staging system, which has far higher limits, so repeated attempts cannot lock you out:

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

When that passes, run the real command from step 2.

Adding another name later

Run the command again with the full list, old names and new:

sudo certbot --nginx -d example.com -d www.example.com -d shop.example.com

Certbot offers to expand the existing certificate. For a separate website on the same server, run it with that site’s names only, and it gets a certificate of its own.

If Cloudflare is in front of the server

With the orange cloud switched on, visitors connect to Cloudflare, and Cloudflare connects to your server. You still want a real certificate on the server, so that second connection is encrypted and verified:

  • Certbot works as described above. Cloudflare passes the validation request through on port 80.
  • In Cloudflare, set SSL/TLS mode to Full (strict). “Flexible” sends traffic to your server unencrypted and causes redirect loops once Nginx redirects to HTTPS.
  • If validation fails only with the proxy on, check that no Cloudflare rule blocks or redirects paths starting with /.well-known/acme-challenge/.

Common mistakes to avoid

  • Closing port 80 after getting the certificate. Renewal fails 60 days later.
  • Forgetting the www name. The certificate then does not cover it and visitors to www see a warning.
  • Leaving a stale AAAA record. Validation goes to the old IPv6 address.
  • Retrying a failing command over and over. Five failures an hour is the limit. Use the certonly --dry-run test until it passes.
  • Copying certificate files to another location. Point Nginx at the files in /etc/letsencrypt/live/. They are links that always lead to the newest certificate; copies go stale at the first renewal.
  • Never testing renewal. One dry run today saves an outage in three months.
  • Waiting for a reminder email. Let’s Encrypt stopped sending them. Monitor the expiry date yourself.

When a free certificate is not enough

A Let’s Encrypt certificate encrypts as well as any paid one. A paid certificate is worth having when you need your organisation’s name verified in it (OV or EV), a warranty, or a year of validity on a system that cannot renew automatically. If you need a wildcard, read what a wildcard certificate covers first: Certbot can issue one, but only with DNS validation, not with the --nginx method used here.

SSL on a SiteHarbour VPS

When we set up a SiteHarbour VPS, our engineers connect your domain and install a free Let’s Encrypt certificate on it as part of the handover, so the first site on the server is already on HTTPS. Use this guide when you add more sites or names yourself. For verified business certificates, see our SSL certificates.

Frequently asked questions

Is a Let’s Encrypt certificate really free?

Yes. There is no charge to issue or renew it, and no limit on how long you can keep renewing.

Do I have to renew it by hand every 90 days?

No. Certbot’s timer checks twice a day and renews any certificate with less than 30 days left. Your only job is to confirm the dry run passes.

Will Certbot break my Nginx configuration?

It edits only the server block that matches your domain and tests the result before reloading. To be safe, keep a copy of the file first, and run sudo nginx -t afterwards.

Can I use one certificate for several domains?

Yes, by listing them all with -d. For unrelated websites it is tidier to give each its own certificate, so one site’s problem cannot block another’s renewal.

What happens if a renewal fails?

Certbot tries again twice a day, so a temporary problem fixes itself. Let’s Encrypt no longer sends expiry reminder emails, so do not rely on being warned: add a certificate expiry check to your monitoring (Uptime Kuma has one built in), or run sudo certbot certificates once a month.

The short version: check DNS (including IPv6), open ports 80 and 443, run one Certbot command, and prove renewal with a dry run. After that the certificate looks after itself.

Share this guide