Mastodon

How to Install an SSL Certificate on Ubuntu with Let's Encrypt (Nginx)

  1. Home
  2. Guides
  3. How to Install an SSL Certificate on Ubuntu with L...

SSL is required. Browsers show a "Not Secure" warning on every site not served over HTTPS, and that warning appears on the first page a visitor lands on. For any site handling personal information or payment details, it actively damages trust. Google has used HTTPS as a ranking signal since 2014. Let's Encrypt provides free, automatically renewable certificates, and Certbot handles the Nginx configuration changes for you; the entire process takes under ten minutes once your domain is pointing at the server.

This guide walks through installing Certbot and the Nginx plugin, obtaining a certificate for your domain, and confirming that automatic renewal is in place. It assumes you have a running Ubuntu 24.04 server with Nginx installed and a domain name with its A record pointing at the server's IP address. The ACME challenge that Let's Encrypt uses to verify domain ownership requires an HTTP request to reach your server on port 80, so DNS must be resolving correctly before running Certbot.

If you have not yet completed the initial VPS setup or set up Nginx (either via the WordPress on Nginx guide or manually), complete those first.

Before you begin: port 80 must be open in UFW. Verify this before continuing:

sudo ufw status

Output:

Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
Nginx Full                 ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
Nginx Full (v6)            ALLOW       Anywhere (v6)

If Nginx Full or Nginx HTTP is not listed, run sudo ufw allow 'Nginx Full' before proceeding.

Step 1 - Installing Certbot

Install Certbot and the Nginx plugin. The Nginx plugin allows Certbot to read your existing Nginx configuration and modify it directly, inserting the certificate paths and HTTPS redirect automatically:

sudo apt install certbot python3-certbot-nginx

Verify the installation completed:

certbot --version

Output:

certbot 2.11.0

The exact version number varies depending on your Ubuntu release and available package versions. Any 2.x output confirms Certbot is installed and ready.

Step 2 - Obtaining and Installing the Certificate

This is the main step: Certbot contacts Let's Encrypt, proves domain ownership via an HTTP challenge, obtains the certificate, and modifies your Nginx configuration to add the certificate paths and an HTTP-to-HTTPS redirect automatically.

Run Certbot with the --nginx flag and specify the domains you want covered. Replace yourdomain.com with your actual domain:

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

Certbot prompts for an email address (used to send renewal expiry warnings) and asks you to agree to the Let's Encrypt terms of service. After agreeing, it completes the ACME challenge to verify domain ownership, obtains the certificate, and modifies your Nginx configuration to add the certificate paths and an HTTP-to-HTTPS redirect.

If the ACME challenge fails, the most common causes are that the domain is not yet pointing to the server, port 80 is blocked by UFW, or Nginx is not running. Verify each of these before re-running the command.

Step 3 - Verifying the Nginx Configuration

Certbot modifies the Nginx server block for your domain to add SSL certificate paths and redirect configuration. Test that the resulting configuration is valid:

sudo nginx -t

Output:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

If the test fails, inspect the configuration file Certbot modified (typically in /etc/nginx/sites-available/) for syntax errors. The most common issue is a malformed server block when the original configuration had non-standard formatting that Certbot's automated edit did not handle cleanly. Reload Nginx to apply the updated configuration:

sudo systemctl reload nginx

Step 4 - Testing the Certificate

With the certificate installed and Nginx reloaded, confirm the certificate is live and the browser accepts it as valid before moving on.

Open a browser and navigate to https://yourdomain.com. The padlock icon in the address bar confirms the connection is encrypted with a valid certificate. Click the padlock to verify the issuer is "Let's Encrypt" and that the expiry date is approximately 90 days from today.

For a command-line test:

curl -I https://yourdomain.com

Output:

HTTP/2 200
server: nginx/1.24.0 (Ubuntu)
content-type: text/html; charset=UTF-8

An HTTP/2 200 response confirms the server is serving the site over HTTPS with a valid certificate. A certificate error at this point usually means Nginx has not been reloaded since Certbot modified the configuration; run sudo systemctl reload nginx and test again.

Step 5 - Confirming HTTP to HTTPS Redirect

Certbot adds an HTTP redirect to the Nginx configuration. Verify it is working:

curl -I http://yourdomain.com

Output:

HTTP/1.1 301 Moved Permanently
Server: nginx/1.24.0 (Ubuntu)
Location: https://yourdomain.com/

A 301 redirect confirms HTTP traffic is being sent to HTTPS. If you do not see this redirect, check your Nginx server block for a return 301 directive in the HTTP (port 80) server block. If it is missing, the HTTP server block should look like:

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;
    return 301 https://$host$request_uri;
}

Step 6 - Confirming Automatic Renewal

Let's Encrypt certificates expire after 90 days. Certbot installs a systemd timer that runs renewal checks twice a day. Verify the timer is active:

systemctl status certbot.timer

Output:

● certbot.timer - Run certbot twice daily
     Loaded: loaded (/lib/systemd/system/certbot.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Mon 2026-06-30 09:00:00 UTC; 1h ago
    Trigger: Mon 2026-06-30 21:00:00 UTC; 12h left
   Triggers: ● certbot.service

If the timer is not active, enable and start it:

sudo systemctl enable certbot.timer
sudo systemctl start certbot.timer

Step 7 - Running a Renewal Dry Run

Test the renewal process without obtaining a new certificate to confirm the configuration is correct end to end:

sudo certbot renew --dry-run

Output:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/yourdomain.com.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Simulating renewal of an existing certificate for yourdomain.com

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

A successful dry run confirms the renewal process is configured correctly and the certificate will renew before expiry without manual intervention. If the dry run fails, the output will identify the specific failure reason.

Common Problems

ACME challenge failed: The domain is not pointing at the server, port 80 is blocked by UFW, or Nginx is returning an error rather than serving the challenge file. Run dig +short yourdomain.com to confirm DNS resolution, and curl -I http://yourdomain.com from an external machine to confirm port 80 is reachable.

Nginx configuration test failed after Certbot: The Certbot Nginx plugin occasionally introduces issues when the existing configuration has non-standard formatting. Open the modified site configuration file (usually at /etc/nginx/sites-available/yourdomain.com), identify the changes Certbot made (they are marked with comment blocks), and compare them against the expected SSL configuration. The certificate files themselves live at /etc/letsencrypt/live/yourdomain.com/.

Certificate not renewing automatically: Confirm the Certbot timer is active with systemctl status certbot.timer. For silent renewal failures, check /var/log/letsencrypt/letsencrypt.log for error detail.

Your Server Is Serving HTTPS

You have obtained a free SSL certificate from Let's Encrypt, configured Nginx to serve HTTPS and redirect HTTP traffic, and confirmed that automatic renewal is in place so the certificate will not expire silently. From here, if you migrated a WordPress site to this server, return to the WordPress migration guide to complete the post-cutover steps.

Get a VPS

Follow along with your own server. Plans from £7.98/month.

View VPS Plans

More Guides

How to Migrate WordPress from Shared Hosting to a VPS

Move a live WordPress site from shared hosting to a VPS: database export with my...

How to Set Up a Fresh Ubuntu 24.04 VPS

Step-by-step: configure a fresh Ubuntu 24.04 VPS from first SSH login to a secur...

Securing Your VPS with UFW and Fail2Ban

How to configure UFW and Fail2Ban on Ubuntu to protect your VPS from port scans...