Free SSL certificate for VPS: the complete guide
HTTPS is no longer optional — it's essential for SEO, security, and user trust. This guide walks you through installing free SSL certificates from Let's Encrypt, configuring Nginx, setting up auto-renewal, and troubleshooting common issues.
Why HTTPS matters (SEO, security, and trust)
HTTPS is critical for modern web. Here's why you need SSL/TLS encryption:
Google ranks HTTPS sites higher than HTTP. Missing HTTPS is an SEO penalty.
Browsers show a warning for non-HTTPS sites. Users distrust unencrypted connections.
Data is encrypted in transit. Attackers cannot intercept passwords, tokens, or sensitive data.
PCI-DSS, HIPAA, and GDPR all require HTTPS for handling user data.
Best practice: Use Let's Encrypt for free SSL certificates with Certbot. They're trusted by all browsers, automatically renewed, and cost nothing.
Prerequisites
Before installing SSL, you need:
Installing Certbot on Ubuntu
Certbot is the official Let's Encrypt client. It automates certificate installation and renewal. Install it with:
sudo apt update sudo apt install certbot python3-certbot-nginx -y
This installs Certbot and the Nginx plugin. The plugin allows Certbot to automatically modify your Nginx config. Verify the installation:
certbot --version
You should see something like certbot 2.x.x.
Getting your first certificate
Certbot can automatically update your Nginx config to use HTTPS. Simply run:
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
Replace yourdomain.com with your actual domain. If you have multiple domains:
sudo certbot --nginx -d domain1.com -d domain2.com -d www.domain1.com
Certbot will:
During installation, Certbot will ask:
That's it! Your certificate is installed. Visit https://yourdomain.com and check the lock icon.
Understanding your certificate files
Certbot stores certificates in /etc/letsencrypt/live/yourdomain.com/. There are four key files:
Critical: Never commit privkey.pem to git. It's the master key to your HTTPS. If exposed, attackers can impersonate your domain.
Nginx SSL configuration
If Certbot didn't automatically configure Nginx (or you want to customize), here's the complete Nginx config:
server {
listen 80;
listen [::]:80;
server_name yourdomain.com www.yourdomain.com;
# Let's Encrypt ACME challenge
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
# Redirect all HTTP to HTTPS
location / {
return 301 https://$server_name$request_uri;
}
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name yourdomain.com www.yourdomain.com;
# SSL certificate paths
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
# SSL security settings
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# HSTS (optional but recommended)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Your app upstream
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}Replace yourdomain.com and localhost:3000 with your values. Test and reload:
sudo nginx -t sudo systemctl reload nginx
Automatic renewal setup
Let's Encrypt certificates expire after 90 days. You need to renew them automatically. Certbot installs a systemd timer that renews certificates automatically.
Check if the timer is active:
sudo systemctl list-timers | grep certbot
You should see output like:
NEXT LEFT LAST PASSED UNIT ACTIVATES Fri 2026-03-07 12:00:00 UTC 4d 14h Wed 2026-03-03 12:00:00 UTC 4d 10h certbot.timer certbot.service
The timer runs daily at a random time and renews certificates that are 30 days or less from expiring. If the timer is not active, enable it:
sudo systemctl enable --now certbot.timer
Test renewal manually (this doesn't actually renew, just does a dry-run):
sudo certbot renew --dry-run
If it succeeds, renewal is set up correctly. Certbot will automatically renew your certificates before they expire.
Testing your SSL certificate
Use SSL Labs to check your certificate and configuration:
Visit https://www.ssllabs.com/ssltest/ and enter your domain. This checks:
- Certificate validity and expiration date
- TLS protocol versions (should be TLSv1.2+)
- Cipher suite strength
- Common vulnerabilities (Heartbleed, DROWN, etc.)
- Certificate chain completeness
Aim for an A or A+ rating. Common issues:
Wildcard certificates
A wildcard certificate covers all subdomains: *.yourdomain.com matches api.yourdomain.com, app.yourdomain.com, etc. Install with:
sudo certbot certonly --manual --preferred-challenges=dns -d yourdomain.com -d '*.yourdomain.com'
This uses manual DNS validation instead of HTTP. Certbot will ask you to add a TXT record to your DNS:
_acme-challenge.yourdomain.com. 300 IN TXT "randomstring123..."
Add this TXT record in your DNS provider's dashboard. Wait for propagation (check with dig _acme-challenge.yourdomain.com), then press Enter in Certbot.
For easier management, use certbot-dns-* plugins (e.g., certbot-dns-route53 for AWS) to automate DNS validation.
Common SSL issues and fixes
- Certbot uses port 80 to verify you own the domain. If DNS doesn't point to your VPS, verification fails.
- Check: nslookup yourdomain.com (should return your VPS IP)
- Update DNS at your registrar (GoDaddy, Namecheap, etc.) to point to your VPS
- Wait 15-30 minutes for DNS propagation (use mxtoolbox.com/dnschecker.com to verify)
- Let's Encrypt needs port 80 (or 443 for standalone) to verify domain ownership
- Check if port 80 is open: sudo ufw status (for UFW firewall) or check your VPS provider's dashboard
- If using UFW, allow port 80: sudo ufw allow 80/tcp
- If using AWS Security Groups / Azure NSG, add inbound HTTP rule
- If you can't open port 80, use DNS validation instead: certbot certonly --manual --preferred-challenges=dns
- Check renewal logs: sudo systemctl status certbot.timer
- Manually test renewal: sudo certbot renew --dry-run (if this fails, the real renewal will fail too)
- Common cause: Port 80 blocked during renewal check. Ensure port 80 stays open.
- Check Certbot service status: sudo journalctl -u certbot.service --no-pager | tail -20
- If renewal is failing, run: sudo certbot renew --force-renewal (this forces immediate renewal)
- Let's Encrypt has rate limits: 50 certificates per domain per week
- If you hit this, wait a week or use a different domain
- This only happens during testing or if you're issuing many certs. Normal renewal is not counted.
- Use staging: certbot --staging to test without hitting rate limits
- Wrong domain in cert - check: openssl x509 -in /etc/letsencrypt/live/yourdomain.com/cert.pem -text
- Using fullchain.pem instead of cert.pem - fix Nginx config to use fullchain.pem
- Nginx not reloaded after cert installation - run: sudo systemctl reload nginx
- Browser cache - clear cache or use incognito mode
- If renewal fails with permission denied, Certbot may need sudo
- Check permissions: ls -la /etc/letsencrypt/ (should be readable by Nginx user)
- Fix ownership: sudo chown -R root:root /etc/letsencrypt/
- Ensure Nginx can read the cert: sudo usermod -aG ssl-cert www-data (if applicable)
How DeployWise automates SSL
Managing SSL certificates across multiple servers and renewals is tedious. DeployWise automates the entire SSL lifecycle:
Instead of manually running Certbot on each server and monitoring renewals, DeployWise handles the entire SSL workflow. Focus on your app, not server certificates.
SSL best practices checklist
Automate SSL certificates across all your servers
DeployWise eliminates manual SSL management. Install certificates, auto-renew, and monitor expiration — all from one dashboard. No more expired certificates.
Launch DeployWise