SSL Certificate Free Setup Guide for Ubuntu 24.04

by David Park
SSL Certificate Free Setup Guide for Ubuntu 24.04

This guide walks you through obtaining and installing a free TLS certificate from Let's Encrypt using Certbot on Ubuntu 24.04, with Nginx as the web server. By the end you will have HTTPS enforced on your domain, auto-renewal configured via a systemd timer, and a verified A-grade TLS setup — all at zero certificate cost.

Prerequisites

  • Ubuntu 24.04 LTS server (root or sudo access)
  • Nginx installed and running (nginx -v returns a version)
  • A registered domain name (e.g., example.com) with an A record pointing to your server's public IP
  • Port 80 and 443 open in your firewall (ufw allow 'Nginx Full')
  • Python 3.12 available (default on Ubuntu 24.04)

Step 1 — Update the Package Index

Run the package update so Certbot installs from current metadata.

sudo apt update

Expected output ends with a line similar to:

Reading package lists... Done

Step 2 — Install Certbot and the Nginx Plugin

Install Certbot and its Nginx plugin from the Ubuntu 24.04 repository. The plugin edits your Nginx config automatically.

sudo apt install -y certbot python3-certbot-nginx

Verify the installation:

certbot --version
certbot 2.9.0

(Your version may be newer; any 2.x release is fine.)

Step 3 — Open Firewall Ports

Allow both HTTP and HTTPS through UFW. Certbot's HTTP-01 challenge requires port 80, and your live site needs port 443.

sudo ufw allow 'Nginx Full'
sudo ufw delete allow 'Nginx HTTP'

Confirm the rules:

sudo ufw status
Status: active

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

Step 4 — Configure a Minimal Nginx Server Block

Certbot needs an existing server block for your domain before it can edit it. Create one if you have not already.

sudo nano /etc/nginx/sites-available/example.com

Paste the following, replacing example.com with your actual domain:

server {
    listen 80;
    listen [::]:80;

    server_name example.com www.example.com;

    root /var/www/example.com/html;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

Enable the site and reload Nginx:

sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Expected output from nginx -t:

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

Step 5 — Obtain and Install the Free SSL Certificate

Run Certbot with the --nginx plugin. Pass both the bare domain and the www subdomain with -d flags. Replace you@example.com with a real address — Let's Encrypt sends expiry warnings there.

sudo certbot --nginx -d example.com -d www.example.com --email you@example.com --agree-tos --no-eff-email

Certbot will:

  1. Verify domain ownership via HTTP-01 challenge.
  2. Download the certificate chain to /etc/letsencrypt/live/example.com/.
  3. Rewrite your Nginx server block to add TLS directives and an HTTP→HTTPS redirect.

Expected final lines:

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 2025-10-01.

Deploying certificate to VirtualHost /etc/nginx/sites-available/example.com
Redirecting all traffic on port 80 to ssl in /etc/nginx/sites-available/example.com

Step 6 — Inspect the Updated Nginx Config

Certbot rewrites the server block. Review what it produced to understand the final state.

cat /etc/nginx/sites-available/example.com

You should see two server blocks: one on port 80 that issues a 301 redirect, and one on port 443 with ssl_certificate and ssl_certificate_key directives pointing to /etc/letsencrypt/live/example.com/.

Reload Nginx to apply any remaining changes:

sudo systemctl reload nginx

Step 7 — Verify Auto-Renewal Is Active

Let's Encrypt certificates expire after 90 days. Certbot installs a systemd timer that renews certificates automatically when they are within 30 days of expiry.

Check the timer status:

sudo systemctl status certbot.timer
● certbot.timer - Run certbot twice daily
     Loaded: loaded (/lib/systemd/system/certbot.timer; enabled; preset: enabled)
     Active: active (waiting) since ...
    Trigger: ...

Confirm the timer is enabled and active (waiting). Run a dry-run renewal to verify the full renewal path works without modifying anything:

sudo certbot renew --dry-run

Expected output:

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

Verify It Works

Run three checks to confirm your SSL certificate free setup is fully operational.

1. HTTPS response check:

curl -I https://example.com
HTTP/2 200
server: nginx/1.24.0 (Ubuntu)
content-type: text/html
...

2. HTTP redirect check:

curl -I http://example.com
HTTP/1.1 301 Moved Permanently
Location: https://example.com/

3. Certificate expiry check:

echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -dates
notBefore=Jul  1 00:00:00 2025 GMT
notAfter=Sep 29 00:00:00 2025 GMT

The notAfter date should be roughly 90 days from today, confirming a valid Let's Encrypt certificate is in place.

Troubleshooting

Certbot fails with "Connection refused" or "Timeout during connect" Port 80 is not reachable. Verify UFW rules (sudo ufw status), check your cloud provider's security group, and confirm Nginx is running (sudo systemctl status nginx).

"No virtual host found" error Certbot cannot locate a server block for your domain. Confirm the symlink exists in /etc/nginx/sites-enabled/ and nginx -t passes before re-running Certbot.

DNS propagation not complete If your A record was created recently, the HTTP-01 challenge may fail. Check propagation with:

dig +short example.com

The output must match your server IP before Certbot can succeed.

certbot renew --dry-run fails after initial setup Check the Certbot log for details:

sudo cat /var/log/letsencrypt/letsencrypt.log | tail -50

Common causes: Nginx is stopped, port 80 is blocked, or the domain's DNS changed.

Certificate is issued but Nginx returns a 404 Your root path (/var/www/example.com/html) does not exist or is empty. Create it and add an index.html:

sudo mkdir -p /var/www/example.com/html
echo '<h1>OK</h1>' | sudo tee /var/www/example.com/html/index.html

Certbot installed but certbot command not found The binary is at /usr/bin/certbot. Add it to your PATH or call it with the full path:

/usr/bin/certbot --version

Next Steps

With your free SSL certificate running and auto-renewal confirmed, consider these hardening steps:

  • Strengthen TLS protocols. Add ssl_protocols TLSv1.2 TLSv1.3; and ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:...; to your server block to drop TLS 1.0/1.1.
  • Enable HSTS. Add add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; inside the port 443 block to instruct browsers to always use HTTPS.
  • Test your grade. Run your domain through SSL Labs' server test (ssllabs.com/ssltest/) to confirm an A or A+ rating.
  • Wildcard certificates. If you run multiple subdomains, Certbot supports wildcard certs via DNS-01 challenge. That requires DNS API credentials and is covered in a separate guide.
  • Monitor expiry. Even with auto-renewal, add an external monitor (UptimeRobot's SSL check is free) so you get alerted if renewal silently fails.

At $0/year for the certificate and roughly 15 minutes of setup time, Let's Encrypt is the only rational choice for indie projects running on a budget.