HTTPS with Let's Encrypt
Chapter 1 — HTTPS with Let's Encrypt
HTTPS encrypts the connection between a visitor and your server. Without it, anyone on the same network can read usernames, passwords, cookies, and session tokens in plain text. Browsers mark plain HTTP sites as "Not Secure." Search engines rank HTTPS sites higher. Let's Encrypt provides free, trusted certificates — and with the DNS-01 challenge, you can get one even when port 80 is blocked by your ISP.
Why HTTPS Matters
The padlock icon is the visible part. What's actually happening underneath is more important:
- Encryption: all data exchanged between browser and server is encrypted. On plain HTTP, a coffee shop Wi-Fi operator, your ISP, or anyone running a packet sniffer can read login credentials and session cookies verbatim.
- Authentication: TLS proves the server is who it claims to be. Without it, a man-in-the-middle attack can serve a fake version of your site to users — they'd have no way to tell.
- Integrity: TLS detects tampering. ISPs are known to inject ads into plain HTTP pages. TLS prevents this.
- HTTP/2 and HTTP/3: both require HTTPS. Without it you're stuck on HTTP/1.1 — significantly slower for sites with many assets.
- Browser warnings: Chrome and Firefox show "Not Secure" in the address bar for HTTP pages with forms. Visitors see this and leave.
- Cloudflare Tunnel already gives you HTTPS for visitors — but understanding TLS and having your own certificate enables Full (Strict) SSL mode and works outside the tunnel for SSH, direct connections, and local network access.
How TLS Works (the short version)
The key concept is the trust chain. Your certificate is trusted because it's signed by Let's Encrypt's intermediate CA, which is trusted because it's signed by a root CA, which is pre-installed in operating systems and browsers. Self-signed certificates break this chain — the browser has no pre-installed trust for them and shows a scary warning instead of a padlock.
Let's Encrypt certificates are Domain Validation (DV) — they prove you control the domain, nothing more. That's sufficient for encryption. Extended Validation (EV) certs that show the company name in the address bar are largely obsolete in modern browsers.
Let's Encrypt and the ACME Protocol
Let's Encrypt is a non-profit Certificate Authority that issues free DV certificates. The process is automated via the ACME protocol — you run certbot, it talks to Let's Encrypt's servers, proves you control the domain, and receives a signed certificate. No forms, no waiting, no credit card.
- Certificate validity: 90 days. Short by design — forces automation and limits damage if a cert is compromised. Auto-renewal handles this transparently.
- Rate limits: 5 certificates per domain per week. For a personal site this is never a problem.
- Wildcard certificates: supported —
*.osztromok.comcovers all subdomains. Requires DNS-01 challenge. - Cost: free, forever. Funded by Mozilla, Google, EFF, and others.
The Challenge Problem — Why HTTP-01 Doesn't Work Here
To prove you control a domain, Let's Encrypt uses a "challenge." You have two main options — and one of them is blocked by Virgin Media.
http://osztromok.com/.well-known/acme-challenge/TOKEN and then fetches it from outside to verify it's really there.Why it fails here:
- Requires port 80 to be reachable from the internet
- Virgin Media Hub 3.0 blocks all inbound connections on port 80
- Cloudflare Tunnel could theoretically help, but certbot and tunnel timing is unreliable
- Bottom line: if you can't receive an inbound HTTP request, HTTP-01 is unavailable
_acme-challenge.osztromok.com TXT "TOKEN". It then queries DNS to verify the token.Why it works here:
- No inbound HTTP required — purely DNS-based
- Works regardless of ISP port blocking
- DNS is on Cloudflare → certbot-dns-cloudflare plugin creates/deletes the TXT record automatically
- Supports wildcard certificates (HTTP-01 cannot)
- Entire process is automated once configured
Installing certbot and the Cloudflare Plugin
Create a Cloudflare API token for DNS editing
In the Cloudflare dashboard → My Profile → API Tokens → Create Token. Use the Edit zone DNS template:
- Permissions: Zone → DNS → Edit
- Zone Resources: Include → Specific zone → osztromok.com
- Copy the token — shown only once.
Getting the Certificate — Complete Walkthrough
--dns-cloudflare-propagation-seconds 60 flag gives DNS time to propagate the TXT record before Let's Encrypt checks it.
/etc/apache2/sites-available/osztromok.com.conf and add the *:443 block below the existing *:80 block:
Wildcard Certificates
A wildcard certificate (*.osztromok.com) covers all first-level subdomains with a single cert. Every new subdomain you add is automatically covered — no re-issuing needed. DNS-01 is the only challenge type that supports wildcards.
*.osztromok.com covers blog.osztromok.com but not www.blog.osztromok.com. For deeper subdomains, request them explicitly. In practice, for a personal site, a wildcard cert for *.osztromok.com covers every subdomain you'll ever create.
Automatic Renewal
Let's Encrypt certificates expire after 90 days. Certbot installs a systemd timer automatically when it's installed on Debian/Ubuntu — you just need to verify it's working.
Reload Apache after renewal
Certbot renews the cert files but doesn't reload Apache automatically. Apache continues serving the old cert from memory until it's reloaded. Add a deploy hook to reload Apache whenever a cert is renewed:
sudo certbot certificates anytime to check the current expiry date.
Cloudflare Origin Certificates — The Alternative
Cloudflare offers their own free certificates called Origin Certificates. Unlike Let's Encrypt, these are issued by Cloudflare's own CA — they're only trusted when traffic goes through Cloudflare's proxy. Browsers connecting directly to your server's IP would see an "untrusted" warning.
| Feature | Let's Encrypt | Cloudflare Origin Cert |
|---|---|---|
| Validity period | 90 days (auto-renewed) | Up to 15 years (set once, forget it) |
| Trusted by browsers | Yes — universally | Only via Cloudflare proxy |
| Works for direct connections | Yes | No — not browser-trusted |
| Works for local network HTTPS | Yes | No |
| Requires renewal automation | Yes (every 90 days) | No (15-year validity) |
| Enables Full (Strict) SSL mode | Yes | Yes |
| Works with Cloudflare Tunnel | Yes | Yes |
Creating a Cloudflare Origin Certificate
Go to Cloudflare dashboard → SSL/TLS → Origin Server → Create Certificate. Choose RSA key, validity 15 years, and add the hostnames (osztromok.com, *.osztromok.com). Cloudflare shows you the cert and key — copy them to your server.
Cloudflare SSL/TLS Modes — Which to Choose
Go to Cloudflare dashboard → SSL/TLS → Overview. This setting controls how Cloudflare communicates with your origin server (the leg behind Cloudflare, not the visitor-to-Cloudflare leg which is always HTTPS).
Testing Your Certificate
ssllabs.com/ssltest and enter osztromok.com. SSL Labs gives a grade (A, A+, B) and flags any configuration weaknesses — weak cipher suites, missing HSTS, certificate chain issues. Aim for an A or A+ rating.
Troubleshooting
dig NS osztromok.com +short — should return Cloudflare nameservers. (2) Propagation delay — increase --dns-cloudflare-propagation-seconds to 120. (3) Wrong API token scope — check the token has Zone → DNS → Edit permission for osztromok.com.
sudo chmod 600 /etc/letsencrypt/cloudflare.ini && sudo chown root:root /etc/letsencrypt/cloudflare.ini. The file must be readable only by root — certbot refuses to use a credentials file that's world-readable as a security measure.
sudo certbot certificates to see the exact paths, then update SSLCertificateFile and SSLCertificateKeyFile in the vhost config accordingly. Also check that the directory is accessible: sudo ls /etc/letsencrypt/live/osztromok.com/.
sudo apache2ctl -M | grep ssl — should show ssl_module. (2) Did Apache restart (not just reload) after enabling mod_ssl? (3) Run the openssl test: echo | openssl s_client -connect localhost:443 -servername osztromok.com 2>/dev/null | grep Verify — should say Verify return code: 0 (ok). (4) Temporarily switch Cloudflare back to Flexible to restore access, then diagnose.
/etc/letsencrypt/renewal/osztromok.com.conf stores the original certbot options. If the Cloudflare API token changed or expired, update it in /etc/letsencrypt/cloudflare.ini and run sudo certbot renew --dry-run again. Also check that the API token hasn't been deleted in the Cloudflare dashboard (My Profile → API Tokens).
Quick Reference — Chapter 1
| Command | Purpose |
|---|---|
| sudo certbot certonly --dns-cloudflare ... | Obtain a cert using Cloudflare DNS-01 challenge — no open ports needed |
| sudo certbot certificates | List all certs managed by certbot with their expiry dates |
| sudo certbot renew --dry-run | Test renewal without actually issuing a new cert — run this monthly |
| sudo systemctl status certbot.timer | Verify the auto-renewal timer is active and see when it next fires |
| sudo a2enmod ssl headers | Enable SSL and headers modules in Apache (restart required after) |
| sudo apache2ctl -M | grep ssl | Confirm ssl_module is loaded in the running Apache |
| echo | openssl s_client -connect localhost:443 -servername osztromok.com 2>/dev/null | grep Verify | Quick cert validity check — "Verify return code: 0 (ok)" = good |
| File / Path | Purpose |
|---|---|
| /etc/letsencrypt/live/osztromok.com/fullchain.pem | Use in Apache SSLCertificateFile — cert + intermediate chain |
| /etc/letsencrypt/live/osztromok.com/privkey.pem | Use in Apache SSLCertificateKeyFile — never share or commit this |
| /etc/letsencrypt/cloudflare.ini | Cloudflare API token for certbot DNS-01 — chmod 600, owner root |
| /etc/letsencrypt/renewal/osztromok.com.conf | Renewal configuration — stores the original certbot options |
| /etc/letsencrypt/renewal-hooks/deploy/ | Scripts here run after every successful renewal — use for Apache reload |
| /var/log/letsencrypt/letsencrypt.log | Full certbot log — first place to look when something goes wrong |