SSL certificate and HTTPS

Install Free SSL on VPS with Let's Encrypt and Certbot

June 2026 · 5 min read

SSL is more than just a padlock icon—it impacts SEO rankings, website trustworthiness, and browser security warnings that drive visitors away. This guide walks you through installing a free SSL certificate on your VPS from start to finish.


What is Let's Encrypt?

Let's Encrypt is a Certificate Authority (CA) that provides free SSL certificates, operated by the Internet Security Research Group (ISRG) and supported by Mozilla, Google, Cisco, and many other organizations.

Certificates last for 90 days, but Certbot automatically renews them—you don't need to manually renew each time.

Prerequisites Before Installation

Installation on Nginx (Ubuntu/Debian)

apt update
apt install certbot python3-certbot-nginx
certbot --nginx -d example.com -d www.example.com

Certbot will ask for an email address for renewal reminders, and whether you want to automatically redirect HTTP traffic to HTTPS. I recommend enabling the redirect.

Installation on Apache (Ubuntu/Debian)

apt install certbot python3-certbot-apache
certbot --apache -d example.com -d www.example.com

Test Automatic Renewal

certbot renew --dry-run

This command simulates renewal without actually issuing a new certificate. It's useful for verifying that your system is properly configured for automatic renewal.

Wildcard Certificates

If you need SSL to cover all subdomains like *.example.com, you must use DNS challenge instead of HTTP challenge:

certbot certonly --manual --preferred-challenges dns \
  -d "*.example.com" -d example.com

The system will ask you to add a TXT record to your DNS before issuing the certificate.

Verify Your Certificate

# View all certificates managed by Certbot
certbot certificates

# Check expiration date using openssl
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -dates

# Verify that auto-renewal timer is running
systemctl status certbot.timer
Timer vs Cron: Ubuntu 22.04+ uses systemd timers instead of cron. Check with systemctl status certbot.timer. If it shows active, renewal is running normally. If not, enable it with systemctl enable certbot.timer --now.

Configure HTTPS Redirect in Nginx

Certbot usually sets up redirect automatically, but here's how to configure it manually if needed:

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

server {
    listen 443 ssl http2;
    server_name example.com www.example.com;
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    # Add HSTS to tell browsers to always use HTTPS
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
}

Common Issues & Troubleshooting

Error: "Connection refused on port 80" or "Challenge failed"

Let's Encrypt must be able to reach http://example.com/.well-known/acme-challenge/ during verification:

# Check if web server is running
systemctl status nginx   # or apache2

# Verify firewall allows port 80
ufw status | grep "80\|ALLOW"

# Test the path from outside
curl -I http://example.com/.well-known/acme-challenge/test
Cloudflare Orange Cloud: Temporarily pause Cloudflare proxy (switch to gray cloud) while issuing the certificate, then re-enable. Certbot cannot read your VPS's real IP if Cloudflare proxy is active.

Error: "Too many failed authorizations" or "Too many certificates already issued"

Let's Encrypt enforces rate limits: 5 certificates per domain per hour, and 50 per week. Use the staging environment to test before issuing a real certificate:

certbot --nginx -d example.com --staging
# If staging succeeds, issue the real certificate (omit --staging)

Certificate Gets Grade B or C on SSL Labs

Add this to your Nginx server block to disable weak protocols and ciphers:

ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;

Test results at ssllabs.com/ssltest/—you should achieve Grade A or A+.

Certbot Renewal Fails: "Port 80 is already in use"

This happens when using standalone mode while the web server is running. Switch to webroot mode instead:

# Edit /etc/letsencrypt/renewal/example.com.conf
authenticator = webroot
webroot_path = /var/www/html

certbot renew --quiet

Wildcard Certificate Request: TXT Record Verification Fails

Certbot requires you to add a TXT record to DNS first, then wait a moment for propagation before pressing Enter:

# Verify TXT record has propagated
dig TXT _acme-challenge.example.com +short

# Wait until it matches the value shown by Certbot, then press Enter

Frequently Asked Questions

Is Let's Encrypt truly free? Are there hidden costs?

Completely free. Let's Encrypt is a non-profit supported by Mozilla, Google, Cisco, and the EFF. The limitation is that it only issues Domain Validated (DV) certificates, not OV or EV certificates that display company names in the browser address bar.

Do I need to manually renew the certificate every 90 days?

No. Certbot installs a systemd timer or cron job automatically during setup. It renews certificates automatically when they have less than 30 days remaining. Test auto-renewal with certbot renew --dry-run (doesn't issue a real certificate).

Does Let's Encrypt support wildcard certificates like *.example.com?

Yes, since 2018. However, wildcard certificates require DNS challenge validation instead of HTTP challenge, and your DNS provider must support a Certbot plugin (e.g., certbot-dns-cloudflare for Cloudflare). Note that *.example.com covers single-level subdomains (mail.example.com) but not nested ones (a.b.example.com).

What happens if a certificate expires without renewal?

Browsers display a "Your connection is not private" warning with NET::ERR_CERT_DATE_INVALID error. Most visitors will leave immediately. Let's Encrypt sends reminder emails 20 and 10 days before expiration to your registered email address.

How do I use Let's Encrypt with a wildcard domain on Cloudflare?

Install the plugin and create a Cloudflare API token with DNS zone edit permissions, then run Certbot:

pip install certbot-dns-cloudflare
# Create ~/.secrets/cloudflare.ini with dns_cloudflare_api_token = YOUR_TOKEN
certbot certonly --dns-cloudflare --dns-cloudflare-credentials ~/.secrets/cloudflare.ini \
  -d "example.com" -d "*.example.com"

Summary

Let's Encrypt eliminated the "SSL is expensive" excuse entirely. There's no reason modern websites should lack HTTPS. The entire process takes less than 10 minutes and runs automatically afterward. If you encounter certificate issuance problems, always check port 80 accessibility, firewall rules, and DNS configuration first.