SSL/TLS Certificates - From Let's Encrypt to Internal CA

Understand certificates, set up free HTTPS, and troubleshoot common issues.

What TLS Certificates Actually Do

TLS (Transport Layer Security, formerly SSL) certificates serve two critical purposes:

  • Encryption: All traffic between client and server is encrypted, preventing eavesdropping on sensitive data.
  • Identity verification: Certificates prove that the server you're connecting to is who it claims to be, preventing man-in-the-middle attacks.

A certificate is essentially a public key signed by a trusted authority (CA) that vouches for the identity of the server owner.

Certificate Types

  • Domain Validated (DV): CA only verifies domain ownership. Fast issuance, free options available (Let's Encrypt). Sufficient for most homelab uses.
  • Organization Validated (OV): CA verifies organization identity. Shows company name in certificate details. Not needed for homelabs.
  • Extended Validation (EV): Strict identity verification, green bar in older browsers. Expensive, not relevant for homelabs.
  • Self-signed: You create and sign your own certificate. Free, instant, but browsers will show security warnings unless you manually trust it.
  • Internal CA: Your own certificate authority for LAN services. Trust it once on your devices, then issue unlimited certificates.

Let's Encrypt with Certbot

Let's Encrypt provides free, automated DV certificates. Here are the main methods:

HTTP-01 Challenge (Port 80)

The simplest method. Certbot places a file at /.well-known/acme-challenge/and Let's Encrypt verifies it via HTTP.

  • Requires port 80 open to the internet (at least temporarily)
  • Works with standalone mode or webroot mode behind Nginx/Apache
  • Cannot issue wildcard certificates
# Standalone mode (stops existing web server)
sudo certbot certonly --standalone -d example.com

# Webroot mode (web server keeps running)
sudo certbot certonly --webroot -w /var/www/html -d example.com

DNS-01 Challenge

Proves domain ownership by creating a DNS TXT record. Required for wildcard certificates.

  • No open ports needed; works behind NAT or firewalls
  • Supports wildcard certificates (*.example.com)
  • Requires DNS provider API access for automation
# Manual DNS challenge
sudo certbot certonly --manual --preferred-challenges dns -d "*.example.com"

# Automated with Cloudflare plugin
sudo certbot certonly --dns-cloudflare \
  --dns-cloudflare-credentials ~/.secrets/cloudflare.ini \
  -d example.com -d "*.example.com"

Renewal

Let's Encrypt certificates expire after 90 days. Certbot sets up automatic renewal:

# Test renewal
sudo certbot renew --dry-run

# Force renewal
sudo certbot renew --force-renewal

# Check timer (systemd)
systemctl list-timers | grep certbot

Cloudflare Origin Certificates

If you use Cloudflare as your DNS and proxy, Origin Certificates are an excellent option:

  • Valid for up to 15 years (no renewal headaches)
  • Free with any Cloudflare plan
  • Only valid between Cloudflare edge and your origin server
  • Requires Cloudflare proxy (orange cloud) to be active

Generate from Cloudflare Dashboard → SSL/TLS → Origin Server → Create Certificate. Install the certificate and private key on your origin server (Nginx Proxy Manager, Traefik, etc.).

Cloudflare Tunnel + Origin Certs

For homelabs using Cloudflare Tunnel, you get automatic HTTPS:

  • Tunnel handles encryption from Cloudflare to your server
  • No ports open, no origin certificates needed
  • Backend services can run on HTTP; tunnel encrypts in transit
  • Optional: Add origin cert for end-to-end encryption even within tunnel

Internal PKI for LAN Services

For services that never touch the internet (internal dashboards, APIs, dev tools), run your own Certificate Authority:

mkcert (Simple)

Zero-config tool that creates a local CA and generates trusted certificates:

# Install mkcert
brew install mkcert  # macOS
apt install mkcert   # Debian/Ubuntu

# Install local CA (adds to system trust store)
mkcert -install

# Generate certificate
mkcert homelab.local "*.homelab.local" 192.168.1.100

Copy the CA certificate to other devices and import into their trust stores for seamless HTTPS on your LAN.

step-ca (Enterprise-grade)

For more complex needs, step-ca provides a full-featured internal CA:

  • ACME protocol support (auto-renewal like Let's Encrypt)
  • Short-lived certificates for zero-trust architectures
  • Integration with Kubernetes, SSH, and more

Troubleshooting Common Issues

Certificate chain incomplete

Browser shows "certificate not trusted" but cert looks valid. Usually missing intermediate certificates.

  • Use SSL Labs test: ssllabs.com/ssltest
  • Ensure your server sends the full chain (cert + intermediates)
  • Let's Encrypt: Use fullchain.pem, not just cert.pem

Certificate expired

  • Check renewal timer/cron is running
  • Verify DNS still points to correct server (for DNS-01)
  • Check certbot logs: /var/log/letsencrypt/letsencrypt.log
  • Manual renewal: certbot renew --force-renewal

Wrong hostname / SAN mismatch

Certificate is valid but browser warns about hostname. The certificate's Subject Alternative Names (SANs) must include the exact hostname you're accessing.

  • Check SANs: openssl x509 -in cert.pem -text | grep -A1 "Subject Alternative"
  • Reissue certificate including all needed hostnames
  • For wildcards: *.example.com covers sub.example.com but not example.com itself

Mixed content warnings

Page loads over HTTPS but includes HTTP resources (images, scripts). Not a certificate issue but often appears alongside HTTPS setup.

  • Update hardcoded http:// URLs to https:// or protocol-relative //
  • Check browser dev tools for specific resources

Certificate Monitoring

Don't let certificates expire unexpectedly:

  • Use the SSL Checker tool on this site to inspect certificates and check expiration dates.
  • Set up monitoring with Uptime Kuma, Prometheus blackbox exporter, or commercial services like cert-manager alerts.
  • Subscribe to Let's Encrypt expiration emails (they notify at 20 days remaining).

Best Practices Summary

  • Use Let's Encrypt for public-facing services; it's free and automated
  • Use Cloudflare Origin Certificates if you're already proxying through Cloudflare
  • Use mkcert or step-ca for internal LAN services
  • Always serve the full certificate chain
  • Monitor expiration dates and set up alerts
  • Test certificates with SSL Labs and browser dev tools
  • Keep private keys secure; never commit them to git
  • Use wildcard certificates sparingly; they're powerful but increase risk if compromised

Validation Checklist

  • All public services show padlock in browser without warnings
  • SSL Labs test shows A or A+ rating
  • Automatic renewal is configured and tested
  • Internal services use trusted internal CA certificates
  • Monitoring alerts are set up for expiration warnings
  • Private keys are stored securely with appropriate permissions

- Crafted by Axiom|Spectre