Certbot is an ACME client that can request a free TLS certificate from Let's Encrypt, install it into an Nginx server block, configure an HTTP-to-HTTPS redirect, and renew the certificate automatically. On Ubuntu 24.04, the safest workflow is to verify DNS and Nginx first, keep port 80 reachable for HTTP-01 validation, install a current Certbot build, request the certificate with the Nginx plugin, and test renewal before treating HTTPS as complete.
Raff users can apply this workflow to a Linux VM running Nginx on Ubuntu 24.04. The tutorial assumes your domain already resolves to the VM and Nginx serves the site over HTTP. If Nginx is not installed yet, complete Install Nginx on Ubuntu 24.04 first.
Let's Encrypt recommends keeping port 80 open for general web servers and redirecting HTTP to HTTPS rather than blocking HTTP entirely. Certbot's current Nginx instructions also support having Certbot edit the Nginx configuration directly, then testing automatic renewal with certbot renew --dry-run.
Prerequisites:
- A Raff Linux VM running Ubuntu 24.04
- Nginx installed and serving the intended hostname over HTTP
- A domain name with DNS records pointing to the VM
- A non-root user with sudo privileges
- Public access to TCP ports 80 and 443
Step 1 — Verify DNS Before Running Certbot
Check the IPv4 records for every hostname you want on the certificate:
dig +short A example.com dig +short A www.example.com
If the domain publishes IPv6, check the AAAA records too:
dig +short AAAA example.com dig +short AAAA www.example.com
Each hostname included in the certificate request must resolve to this server. A stale AAAA record can send validation traffic to the wrong IPv6 host even when the A record is correct.
Compare the DNS result with the VM's public address. If a hostname should not be used, do not include it in the Certbot command.
Verify: All requested hostnames should resolve to the intended server, and any published AAAA record should reach the same Nginx deployment.
Step 2 — Verify Nginx Serves the Domain Over HTTP
Check the active Nginx configuration:
sudo nginx -t
Confirm the site responds over HTTP:
curl -I http://example.com
Inspect the server block if needed:
sudo nginx -T 2>/dev/null | grep -A8 -B3 'server_name example.com'
The active server block should contain the hostname you plan to request with Certbot, for example:
server { listen 80; listen [::]:80; server_name example.com www.example.com; root /var/www/example.com/public; }
Certbot's Nginx plugin needs to identify the matching server block before it can install the certificate cleanly.
Verify: sudo nginx -t should succeed and curl -I http://example.com should return an HTTP response from the intended Nginx site.
Step 3 — Keep Ports 80 and 443 Reachable
Check UFW status:
sudo ufw status numbered
If UFW is active, allow SSH and the Nginx profile for HTTP and HTTPS:
sudo ufw allow OpenSSH sudo ufw allow 'Nginx Full'
Review the result:
sudo ufw status numbered
Do not close port 80 after enabling HTTPS when using the HTTP-01 validation path. Let's Encrypt recommends serving HTTP and redirecting it to HTTPS.
Also verify that any upstream cloud firewall or network policy allows TCP 80 and 443 to this VM.
Verify: The server remains reachable over SSH, HTTP reaches port 80, and HTTPS traffic is allowed on port 443.
Step 4 — Install Certbot for Nginx on Ubuntu 24.04
Certbot's current Linux instructions support installing the Certbot snap maintained by the Certbot team. Avoid mixing multiple Certbot installation methods on the same server.
First check for an existing installation:
command -v certbot || true snap list certbot 2>/dev/null || true dpkg -l | grep -E 'certbot|python3-certbot-nginx' || true
If Certbot is already managing production certificates successfully, do not replace it only for this tutorial. Continue with the existing supported installation and verify renewal.
For a new installation, make sure snap is available:
command -v snap || sudo apt install -y snapd
Install Certbot:
sudo snap install --classic certbot
Create the standard command path if needed:
sudo ln -sf /snap/bin/certbot /usr/local/bin/certbot
Check the installed version and Nginx plugin:
certbot --version certbot plugins | grep -A3 nginx
Do not depend on a specific Certbot version string in automation because the package is updated over time.
Verify: certbot --version should succeed and certbot plugins should list the Nginx plugin.
Step 5 — Request and Install a Let's Encrypt Certificate with Certbot and Nginx
Request the certificate and let Certbot update the matching Nginx server block:
sudo certbot --nginx --redirect \ -d example.com \ -d www.example.com
Certbot will ask for an email address and agreement to the Let's Encrypt terms. The --redirect option configures HTTP requests to redirect to HTTPS after successful issuance.
If you use only the apex hostname, request only that name:
sudo certbot --nginx --redirect -d example.com
Do not request hostnames that do not resolve to this server.
After issuance, list managed certificates:
sudo certbot certificates
Do not manually edit files inside /etc/letsencrypt/live/ or /etc/letsencrypt/renewal/; Certbot manages those paths.
Verify: sudo certbot certificates should list the requested hostname and sudo nginx -t should still pass.
Step 6 — Verify HTTPS, the HTTP Redirect, and the Certificate
Test HTTPS:
curl -I https://example.com
Test the HTTP redirect:
curl -I http://example.com
The HTTP response should redirect to the HTTPS URL, commonly with 301 or 308.
Inspect the certificate presented by Nginx:
openssl s_client \ -connect example.com:443 \ -servername example.com \ </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates
Check that the certificate matches the hostname and has valid start and expiry dates.
Verify: HTTPS should complete without a certificate error, HTTP should redirect to HTTPS, and the certificate dates should be valid.
Step 7 — Test Certbot Automatic Renewal for Nginx
Most Certbot installations include a scheduled renewal task. Check both common scheduling paths:
systemctl list-timers --all | grep -i certbot || true sudo grep -R 'certbot renew' /etc/crontab /etc/cron.* 2>/dev/null || true
Run Certbot's renewal simulation:
sudo certbot renew --dry-run
This tests the renewal path without replacing your active production certificate.
Review managed certificate details again:
sudo certbot certificates
You normally should not create an extra custom cron job when your Certbot installation already provides automatic renewal.
Verify: sudo certbot renew --dry-run should complete successfully and a scheduled renewal mechanism should be present for the installation method in use.
Step 8 — Add Security Headers Without Breaking the Site
TLS does not automatically configure application security headers. Add only headers that match your application's requirements.
Open the active HTTPS server block and add a conservative baseline inside the server block:
add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header X-Frame-Options "SAMEORIGIN" always;
X-Frame-Options: SAMEORIGIN is not appropriate when the site must be embedded by another origin.
Treat HSTS separately. Enable it only after HTTPS, redirects, certificate renewal, and every required hostname are working reliably. Start with a short test duration:
add_header Strict-Transport-Security "max-age=300" always;
Do not add includeSubDomains until every required subdomain supports HTTPS.
Validate and reload:
sudo nginx -t sudo systemctl reload nginx
Check the headers:
curl -I https://example.com
Verify: nginx -t should pass, the site should remain reachable over HTTPS, and only the intended headers should appear.
Step 9 — Troubleshoot Common Certbot and Nginx Errors
Certbot cannot find a matching Nginx server block
Confirm the hostname appears in an enabled server block:
sudo nginx -T 2>/dev/null | grep -n 'server_name'
Correct server_name, then run:
sudo nginx -t sudo systemctl reload nginx
The Nginx plugin is not installed
Check available plugins:
certbot plugins
With the Certbot snap, the Nginx plugin should be included. If Certbot comes from another installation method, follow the instructions for that method rather than mixing packages.
HTTP-01 validation fails
Check DNS and port 80:
dig +short A example.com dig +short AAAA example.com sudo ss -ltnp | grep ':80' sudo ufw status numbered curl -I http://example.com
A wrong A/AAAA record, blocked port 80, or incorrect Nginx routing can all break validation.
/.well-known/acme-challenge/ returns 404
If a custom Nginx rule intercepts hidden paths or rewrites all requests, inspect the active configuration before retrying:
sudo nginx -T 2>/dev/null | grep -n -E 'well-known|rewrite|return|location'
Fix the conflicting routing rule instead of repeatedly requesting new certificates.
Certbot hits a Let's Encrypt rate limit
Stop retrying the same broken production request. Fix DNS, firewall, or Nginx first. Use Certbot's dry-run or staging workflow while validating renewal behavior.
Renewal works but Nginx serves an old certificate
Check the certificate Certbot manages, validate Nginx, and reload it:
sudo certbot certificates sudo nginx -t sudo systemctl reload nginx
Then inspect the live certificate again with openssl s_client.
Verify: After troubleshooting, sudo nginx -t, curl -I https://example.com, and sudo certbot renew --dry-run should all succeed.