Installing SSL with Let's Encrypt and Certbot

Free SSL certificates on Rocky Linux 9 with Certbot — installation, Nginx config update, auto-renewal setup, and what to do when you run certbot and nothing seems to change.

Browser showing padlock icon on a WordPress site — HTTPS working after Certbot installation

Before Running Certbot

Two requirements:

1. Your domain must be pointing to this server. Certbot verifies ownership by making an HTTP request to your domain. If DNS hasn’t propagated yet, certbot fails. Point the domain first (Part 5.5), wait for propagation, then run certbot.

2. Port 80 must be open. Certbot’s HTTP challenge uses port 80. Verify it’s open:

sudo firewall-cmd --list-services
# Should include: http

If not:

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --reload

Step 1 — Install Certbot

sudo dnf install certbot python3-certbot-nginx -y

The python3-certbot-nginx package is the Nginx plugin — it lets certbot read and modify your Nginx config directly.


Step 2 — Run Certbot

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

What happens when you run this:

  1. Certbot contacts Let’s Encrypt servers
  2. Places a temporary verification file on your server
  3. Let’s Encrypt makes an HTTP request to verify you control the domain
  4. Certificate is issued and saved to /etc/letsencrypt/live/yourdomain.com/
  5. Certbot reads your Nginx config and adds the SSL configuration
  6. Certbot asks whether to redirect HTTP to HTTPS — choose 2 (Redirect)

The output ends with something like:

Congratulations! You have successfully enabled HTTPS on https://yourdomain.com
Terminal showing certbot success output — certificate path, expiry date, and HTTPS confirmation
Certbot success output. The certificate path and expiry date are shown. 90-day certificates renewed automatically before they expire.

What Certbot Actually Does to Nginx

This is the part that confused me initially. After certbot runs successfully, open your Nginx server block:

sudo cat /etc/nginx/conf.d/yourdomain.conf

Certbot has added several lines — SSL certificate paths, HTTPS listener, and an HTTP redirect block. It looks something like this (simplified):

server {
    listen 443 ssl;
    server_name yourdomain.com www.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    # ... your original server block content ...
}

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

The second server block is the redirect — any HTTP request gets permanently redirected to HTTPS. This is why refreshing the browser after certbot should show HTTPS.


When Nothing Changes After Running Certbot

This happens when certbot generates the certificate but fails to update the Nginx config correctly — usually because the server block structure is non-standard.

Check if certbot added the SSL config:

grep "ssl_certificate" /etc/nginx/conf.d/yourdomain.conf

If nothing is returned, certbot didn’t update the config. Add the SSL and redirect blocks manually:

# Add to your existing server block
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

Add a separate HTTP redirect block:

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

Then test and reload:

sudo nginx -t && sudo systemctl reload nginx

Also Open Port 443

sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload

If port 443 isn’t open, HTTPS connections are blocked at the firewall level and the browser shows a connection timeout.


Step 3 — Verify HTTPS is Working

Visit your domain in a browser: https://yourdomain.com

You should see the padlock icon in the address bar. Click it to see certificate details — it should show “Let’s Encrypt” as the issuer.

Test with curl:

curl -I https://yourdomain.com

Response should show HTTP/2 200 or HTTP/1.1 200 OK — not a certificate error.


Step 4 — Set Up Auto-Renewal

Let’s Encrypt certificates expire after 90 days. Certbot installs a systemd timer for automatic renewal:

sudo systemctl status certbot-renew.timer

If it shows active, renewal is handled automatically. Certbot renews certificates when they’re within 30 days of expiry.

Test that renewal works:

sudo certbot renew --dry-run

--dry-run simulates the renewal process without actually renewing. If it completes without errors, auto-renewal will work when the time comes.

If the timer isn’t active, set up a cron job instead:

sudo crontab -e

Add this line — runs certbot at 2am daily, only renews if needed:

0 2 * * * /usr/bin/certbot renew --quiet

Save and exit. The --quiet flag suppresses output unless there’s an error.


The 3-Month Expiry Problem

The first time a certificate expires with a red browser warning, it’s usually because the renewal command was never confirmed to work. The fix is one command:

sudo certbot renew

After fixing it, run --dry-run to verify auto-renewal is properly configured so it doesn’t happen again.

The habit: after installing a new certificate, immediately run certbot renew --dry-run to confirm the renewal pipeline works. Takes 10 seconds and avoids the 3am emergency when a client site shows red.

Frequently Asked Questions

Why does the site still show HTTP after running certbot?
Certbot generates the certificate but doesn't automatically add the HTTP-to-HTTPS redirect. You need to add a redirect block to your Nginx config: a second server block listening on port 80 that returns a 301 redirect to HTTPS. Certbot's --nginx flag should handle this, but if the server block structure is non-standard it sometimes doesn't.
How do I set up SSL auto-renewal?
Certbot installs a systemd timer (certbot-renew.timer) that handles renewal automatically. Verify it's active: systemctl status certbot-renew.timer. If you prefer cron, add: 0 2 * * * /usr/bin/certbot renew --quiet to your crontab. Test with: certbot renew --dry-run.
What happens when SSL expires?
Browsers show a red warning and block visitors from accessing the site. The site still works over HTTP. Renew with: sudo certbot renew. If auto-renewal was set up correctly, this shouldn't happen — but if it does, one command fixes it.
Does my domain need to be pointing to the server before running certbot?
Yes. Certbot verifies domain ownership by making an HTTP request to your domain. If the domain doesn't resolve to your server's IP yet, certbot will fail with a connection error. Point the domain first, wait for DNS to propagate, then run certbot.