SSL/TLS certificates enable HTTPS. Here’s everything you need to know about managing them.
Certificate Types
Domain Validated (DV)
- Verifies domain ownership only
- Cheapest/free (Let’s Encrypt)
- Good for most sites
Organization Validated (OV)
- Verifies organization exists
- More trust indicators
- Good for businesses
Extended Validation (EV)
- Extensive verification
- Green bar (historically)
- Expensive, decreasing value
Certificate Anatomy
-----BEGIN CERTIFICATE-----
MIIFazCCBFOgAwIBAgISA...
-----END CERTIFICATE-----
Contains:
- Subject (your domain)
- Issuer (who signed it)
- Validity period
- Public key
- Signature
Let’s Encrypt with Certbot
Free, automated certificates:
Installation
1
2
3
4
5
|
# Ubuntu/Debian
apt install certbot
# With nginx plugin
apt install python3-certbot-nginx
|
Get Certificate
1
2
3
4
5
6
7
8
|
# Standalone (stops web server)
certbot certonly --standalone -d example.com -d www.example.com
# Webroot (no downtime)
certbot certonly --webroot -w /var/www/html -d example.com
# Nginx plugin (automatic config)
certbot --nginx -d example.com -d www.example.com
|
Auto-Renewal
1
2
3
4
5
|
# Test renewal
certbot renew --dry-run
# Cron job (usually auto-installed)
0 0,12 * * * certbot renew --quiet
|
Certificate Files
Let’s Encrypt creates:
/etc/letsencrypt/live/example.com/
├── cert.pem # Your certificate
├── chain.pem # Intermediate certificates
├── fullchain.pem # cert.pem + chain.pem (use this)
└── privkey.pem # Private key (keep secret!)
Nginx Configuration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
server {
listen 443 ssl;
http2 on;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Strong SSL settings
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
}
|
Wildcard Certificates
Cover all subdomains:
1
2
3
|
# Requires DNS challenge
certbot certonly --manual --preferred-challenges dns \
-d example.com -d *.example.com
|
You’ll need to add a TXT record to your DNS.
Automated with DNS Provider
1
2
3
4
|
# Cloudflare example
certbot certonly --dns-cloudflare \
--dns-cloudflare-credentials ~/.cloudflare.ini \
-d example.com -d *.example.com
|
Checking Certificates
1
2
3
4
5
6
7
8
9
10
11
|
# View certificate details
openssl x509 -in cert.pem -text -noout
# Check expiration
openssl x509 -in cert.pem -noout -dates
# Check remote server
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates
# Test SSL configuration
# https://www.ssllabs.com/ssltest/
|
Common Issues
Certificate Chain Incomplete
SSL_ERROR_UNKNOWN_CA_ALERT
Fix: Use fullchain.pem, not just cert.pem.
Certificate Expired
1
2
3
4
5
|
# Check expiry
openssl x509 -in cert.pem -noout -enddate
# Force renewal
certbot renew --force-renewal
|
Key Mismatch
1
2
3
4
|
# Verify key matches certificate
openssl x509 -noout -modulus -in cert.pem | md5sum
openssl rsa -noout -modulus -in key.pem | md5sum
# Should match
|
Monitoring Expiration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#!/bin/bash
DOMAIN="example.com"
DAYS=30
expiry=$(echo | openssl s_client -connect $DOMAIN:443 2>/dev/null | \
openssl x509 -noout -enddate | cut -d= -f2)
expiry_epoch=$(date -d "$expiry" +%s)
now_epoch=$(date +%s)
days_left=$(( (expiry_epoch - now_epoch) / 86400 ))
if [ $days_left -lt $DAYS ]; then
echo "Certificate expires in $days_left days!"
# Send alert
fi
|
Docker with SSL
1
2
3
4
5
6
7
8
|
services:
nginx:
image: nginx
ports:
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- /etc/letsencrypt:/etc/letsencrypt:ro
|
Summary
- Use Let’s Encrypt (free, automated)
- Enable auto-renewal
- Use
fullchain.pem for the certificate
- Monitor expiration
- Test your SSL configuration
SSL certificates used to be complex and expensive. Now there’s no excuse not to use HTTPS everywhere.
Comments