What You'll End Up With
This free SSL certificate setup guide walks you through issuing a trusted TLS certificate from Let's Encrypt, installing it on Nginx running on Ubuntu 24.04, and configuring automatic renewal. When you're done, your domain will serve HTTPS with a valid certificate, HTTP will redirect to HTTPS, and a systemd timer will renew the cert before it expires — no manual intervention required.
Prerequisites:
- Ubuntu 24.04 server (root or sudo user)
- Nginx installed and running (
nginx -vreturns a version) - A 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
- No existing certificate on the domain
Step 1 — Update Packages and Install Certbot
Certbot is the official Let's Encrypt client. Install it and the Nginx plugin from the Ubuntu package repository.
1.1 Refresh the package index.
sudo apt update
1.2 Install Certbot and the Nginx plugin.
sudo apt install -y certbot python3-certbot-nginx
Expected output ends with:
Setting up python3-certbot-nginx (2.x.x) ...
1.3 Confirm the installed version.
certbot --version
certbot 2.x.x
Step 2 — Configure a Minimal Nginx Server Block
Certbot needs a valid server_name directive to locate and modify the correct Nginx configuration. If you don't already have a server block for your domain, create one now.
2.1 Create a new site configuration file.
sudo nano /etc/nginx/sites-available/example.com
2.2 Paste the following minimal block. Replace example.com with your actual domain.
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example.com/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
2.3 Enable the site by creating a symlink.
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
2.4 Test the Nginx configuration for syntax errors.
sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
2.5 Reload Nginx to apply the new server block.
sudo systemctl reload nginx
Step 3 — Open Firewall Ports
Let's Encrypt's HTTP-01 challenge requires port 80. HTTPS traffic uses port 443. Both must be reachable from the internet.
3.1 Allow HTTP and HTTPS through UFW.
sudo ufw allow 'Nginx Full'
Rule added
Rule added (v6)
3.2 If you previously had only port 80 open, remove the redundant rule.
sudo ufw delete allow 'Nginx HTTP'
3.3 Verify the firewall status.
sudo ufw status
Status: active
To Action From
-- ------ ----
OpenSSH ALLOW Anywhere
Nginx Full ALLOW Anywhere
OpenSSH (v6) ALLOW Anywhere (v6)
Nginx Full (v6) ALLOW Anywhere (v6)
Step 4 — Issue the Free SSL Certificate
Run Certbot with the --nginx plugin. It will authenticate your domain via the HTTP-01 challenge, obtain the certificate from Let's Encrypt, and automatically modify your Nginx configuration to enable HTTPS.
4.1 Run Certbot for your domain. Replace example.com with your domain.
sudo certbot --nginx -d example.com -d www.example.com
Certbot will prompt you for:
- An email address for renewal and security notices — enter a real address you monitor.
- Agreement to the Let's Encrypt Terms of Service — type
Y. - Whether to share your email with EFF — optional, type
Nto decline.
Expected output on success:
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-XX-XX.
These files will be updated when the certificate renews.
Deploying certificate
Successfully deployed certificate for example.com to /etc/nginx/sites-available/example.com
Successfully deployed certificate for www.example.com to /etc/nginx/sites-available/example.com
Congratulations! You have successfully enabled HTTPS on https://example.com and https://www.example.com
4.2 Inspect the Nginx config Certbot modified to confirm the SSL directives were added.
sudo cat /etc/nginx/sites-available/example.com
You should see listen 443 ssl;, ssl_certificate, and ssl_certificate_key lines, plus an HTTP-to-HTTPS redirect block.
Step 5 — Harden the SSL Configuration
Certbot's default SSL parameters are acceptable, but adding strong cipher settings and HTTP Strict Transport Security (HSTS) raises your security posture and improves SSL Labs grades.
5.1 Open the Nginx SSL parameters snippet Certbot created.
sudo nano /etc/letsencrypt/options-ssl-nginx.conf
Verify it contains at least:
ssl_session_cache shared:le_nginx_SSL:10m;
ssl_session_timeout 1440m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
If TLSv1 or TLSv1.1 appear, remove them.
5.2 Add an HSTS header inside the server block that listens on port 443 in /etc/nginx/sites-available/example.com.
sudo nano /etc/nginx/sites-available/example.com
Add this line inside the server { listen 443 ssl; ... } block:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
5.3 Test and reload Nginx.
sudo nginx -t && sudo systemctl reload nginx
Step 6 — Verify Automatic Renewal
Let's Encrypt certificates expire after 90 days. Certbot installs a systemd timer that runs renewal checks twice daily. Confirm it is active and perform a dry run.
6.1 Check the Certbot 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: ...
6.2 Run a renewal dry run to confirm the process works end-to-end without issuing a new certificate.
sudo certbot renew --dry-run
Congratulations, all simulated renewals succeeded:
/etc/letsencrypt/live/example.com/fullchain.pem (success)
6.3 Check when the certificate actually expires.
sudo certbot certificates
Found the following certs:
Certificate Name: example.com
Domains: example.com www.example.com
Expiry Date: 2025-XX-XX (VALID: 89 days)
Certificate Path: /etc/letsencrypt/live/example.com/fullchain.pem
Private Key Path: /etc/letsencrypt/live/example.com/privkey.pem
Verify It Works
Run these checks after completing all steps.
Browser check: Navigate to https://example.com. The padlock icon should appear with no certificate warnings.
HTTP redirect check: Confirm HTTP redirects to HTTPS.
curl -I http://example.com
HTTP/1.1 301 Moved Permanently
Location: https://example.com/
Certificate details via OpenSSL:
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -issuer -dates
issuer=C=US, O=Let's Encrypt, CN=R11
notBefore=2025-XX-XX ...
notAfter=2025-XX-XX ...
SSL Labs grade (optional): Submit your domain at https://www.ssllabs.com/ssltest/. Expect an A rating with the hardening from Step 5.
Troubleshooting
Certbot fails with "Connection refused" or "Timeout during connect"
Port 80 is blocked. Verify UFW allows Nginx Full (sudo ufw status) and that your hosting provider's external firewall or security group also allows port 80.
"No names were found in your configuration files" error
Your Nginx server block is missing a server_name directive or the file is not symlinked into sites-enabled. Re-check Step 2.
"Too many certificates already issued for exact set of domains"
Let's Encrypt enforces a limit of 5 duplicate certificates per week. Wait until the rate limit resets, or add --staging to test with a non-trusted certificate.
Nginx fails to reload after Certbot modifies config
Run sudo nginx -t to identify the syntax error. Certbot sometimes adds duplicate ssl_certificate lines if the block was already partially configured. Remove duplicates manually.
Dry run succeeds but live renewal fails
Check the Certbot log at /var/log/letsencrypt/letsencrypt.log. Common causes: DNS propagation lag after an IP change, or port 80 blocked between renewal attempts.
Certificate shows as expired in browser despite renewal
Nginx may be serving a cached configuration. Run sudo systemctl reload nginx after any renewal. Certbot's deploy hook handles this automatically, but verify the hook exists:
cat /etc/letsencrypt/renewal-hooks/deploy/nginx
If missing, create it:
echo -e '#!/bin/bash\nsystemctl reload nginx' | sudo tee /etc/letsencrypt/renewal-hooks/deploy/nginx
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/nginx
Next Steps
With your free SSL certificate setup complete, consider these follow-on tasks:
- Enable HTTP/2 — Add
http2to thelisten 443directive (listen 443 ssl http2;) for faster asset loading. - Set up multiple domains — Re-run
certbot --nginx -d domain2.comfor each additional domain; each gets its own certificate. - Monitor expiry — Add a cron job or use a monitoring tool to alert you if a certificate is fewer than 14 days from expiry, catching any renewal failures early.
- Wildcard certificates — If you need
*.example.com, use the DNS-01 challenge (certbot certonly --manual --preferred-challenges dns -d '*.example.com') and update DNS TXT records as prompted.