To configure SSL on Nginx with Let's Encrypt, install Certbot (the official ACME client), run sudo certbot --nginx to obtain and auto-install a free, trusted certificate, then let Certbot's systemd timer renew it automatically before it expires. Let's Encrypt certificates are valid for 90 days, so automation is the whole point — and as of 2026 you no longer touch cron, OpenSSL by hand, or any "preview" sign-up.
This guide is fully updated for 2026 practice: the modern Certbot install paths, the Nginx plugin flow, TLS 1.2/1.3 hardening using the Mozilla SSL Configuration Generator, wildcard certificates via the DNS-01 challenge, and automatic renewal via systemd timers. (Let's Encrypt left its limited beta back in 2016 — there is no longer a domain whitelist or "dev preview" to register for.)
Why HTTPS is non-negotiable
HTTPS is now a baseline, not a nice-to-have:
- Browser trust — Chrome, Firefox and Safari flag plain HTTP pages as "Not secure".
- SEO — HTTPS is a confirmed Google ranking signal, and Core Web Vitals favour the HTTP/2 and HTTP/3 protocols, which require TLS.
- Security & privacy — TLS protects credentials, sessions and API payloads from interception and tampering.
- Compliance — PCI DSS, SOC 2, GDPR and most procurement checklists expect encryption in transit.
How Let's Encrypt works in 2026
Let's Encrypt is a free, automated, open Certificate Authority run by the non-profit Internet Security Research Group (ISRG). It issues Domain Validated (DV) certificates using the ACME protocol, which Certbot speaks on your behalf.
Key facts to design around:
- Free — there is no charge for the certificates themselves (you only pay for your own server and DNS).
- 90-day lifetime — short by design, which forces automated renewal.
- Rate limits — currently 50 certificates per registered domain per week, plus 5 duplicate certificates per week. Test against the staging environment (or
--dry-run) to avoid burning them. - Shorter lifetimes are coming — the CA/Browser Forum has voted to cut the maximum TLS certificate lifetime to 47 days by 2029. Let's Encrypt is already piloting 6-day short-lived certificates. Either way, automation is mandatory, not optional.
Prerequisites
- A Linux server (Ubuntu/Debian, RHEL/Alma/Rocky, etc.) running Nginx.
- A registered domain with an A/AAAA record pointing to the server's public IP.
- Inbound ports 80 and 443 open (HTTP-01 validation uses port 80).
sudo/root access.
Step 1 — Install Certbot
The recommended path is the snap package, which ships the latest Certbot and a built-in renewal timer on virtually every distro:
# Recommended: install Certbot via snap
sudo snap install core && sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbotOn Debian/Ubuntu you can also use distro packages, which include the Nginx plugin and a systemd renewal timer:
# Alternative: distro packages (Debian/Ubuntu)
sudo apt update
sudo apt install certbot python3-certbot-nginxAvoid the legacy methods. Cloning the letsencrypt repo and running ./letsencrypt-auto, or pip install certbot, are deprecated and unmaintained — don't use them. The python3-certbot-nginx plugin (bundled with snap) is what lets Certbot read and edit your Nginx config automatically.
Step 2 — Issue and install the certificate
The Nginx plugin does everything in one command: it proves you control the domain (HTTP-01), writes the certificate paths into your server block, and offers to add an HTTP→HTTPS redirect.
sudo certbot --nginx -d example.com -d www.example.comCertbot 2.x and later default to ECDSA (P-256) private keys, which are smaller and faster than RSA. Certificates are written to /etc/letsencrypt/live/<domain>/ (fullchain.pem and privkey.pem).
If you'd rather keep full control of your Nginx config and only obtain the certificate, use certonly with an explicit challenge method:
# webroot: server is already serving the domain over HTTP
sudo certbot certonly --webroot -w /var/www/example.com -d example.com
# standalone: no web server running yet; Certbot binds port 80 itself
sudo certbot certonly --standalone -d example.comChoosing a challenge: HTTP-01 vs DNS-01
The challenge type decides how Certbot proves domain control — and only one of them can issue wildcard certificates.
| Challenge | How it validates | Wildcard? | Best for |
|---|---|---|---|
| HTTP-01 | Serves a token over port 80 | No | Single hostnames on a public web server |
| DNS-01 | Adds a TXT record in DNS | Yes (*.example.com) |
Wildcards, hosts not reachable on port 80, internal services |
| TLS-ALPN-01 | Special TLS handshake on port 443 | No | Port 80 blocked but 443 open |
For a wildcard certificate you must use DNS-01 — HTTP-01 cannot issue one:
# Wildcard via DNS-01 (manual TXT record)
sudo certbot certonly --manual --preferred-challenges dns \
-d '*.example.com' -d example.comManual DNS-01 won't renew unattended. For hands-off wildcard renewal, use a DNS plugin for your provider — for example certbot-dns-cloudflare or certbot-dns-route53 — so Certbot can publish the TXT record itself on every renewal.
Step 3 — Harden the Nginx TLS config
Don't ship the defaults. Enable TLS 1.2 and 1.3 only (1.0 and 1.1 are deprecated and disabled in modern browsers), and use the "intermediate" profile from the Mozilla SSL Configuration Generator. The server block below is a clean, copy-pasteable starting point:
# /etc/nginx/sites-available/example.com
# HTTP -> HTTPS redirect
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on; # modern directive (Nginx 1.25.1+)
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;
# Mozilla "intermediate" profile
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:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
# Session resumption
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off;
# HSTS — add ' preload' only once you commit to the preload list
add_header Strict-Transport-Security "max-age=63072000" always;
root /var/www/example.com;
index index.html;
}A few 2026-specific notes:
- HTTP/2 is now enabled with
http2 on;— the oldlisten 443 ssl http2;syntax is deprecated. - OCSP stapling is no longer needed for Let's Encrypt certificates. Let's Encrypt stopped including OCSP URLs in 2025 and is moving to CRLs, so
ssl_stapling on;is effectively a no-op for these certs — you can safely omit it. - ECDSA + RSA — if you need to support very old clients, you can issue both key types and reference both certificate pairs; for most modern audiences ECDSA alone is fine.
Test and reload:
sudo nginx -t
sudo systemctl reload nginxStep 4 — Automatic renewal (systemd timer, not cron)
This is the biggest change from older guides: do not add a cron line. Modern Certbot installs a systemd timer (or, with the snap, its own internal timer) that runs twice a day and renews any certificate within 30 days of expiry. Verify it and test the renewal end-to-end:
# Confirm the renewal timer is active
systemctl list-timers | grep certbot
systemctl status certbot.timer
# Dry run: exercises the full renewal without touching live certs or rate limits
sudo certbot renew --dry-runIf you hand-edited the Nginx config (instead of letting the --nginx plugin manage it), add a deploy hook so Nginx reloads after each successful renewal and picks up the new certificate:
sudo certbot renew --deploy-hook "systemctl reload nginx" --dry-runVerify your setup
Check the redirect and certificate from the command line, then confirm the grade externally:
# Should return a 301 to the https:// URL
curl -sI http://example.com | grep -i location
# Inspect the served certificate and protocol
curl -sv https://example.com 2>&1 | grep -Ei 'subject:|TLSv1|issuer:'Round it out with an external scan — SSL Labs or the open-source testssl.sh — and aim for an A/A+ rating with no weak protocols or ciphers reported.
Taking this to production
A single certbot --nginx is enough for one box, but production estates add real complexity: load balancers and CDNs that terminate TLS, blue/green deploys, multi-region certificates, secrets management, and renewal monitoring so a silent failure never becomes an outage.
MicroPyramid has spent 12+ years and 50+ delivered projects building and operating this kind of infrastructure. We frequently terminate TLS at an AWS Application Load Balancer or CloudFront with ACM, while keeping Let's Encrypt + Certbot for internal and origin services — as part of our AWS consulting services and cloud migration services. If your TLS termination sits in front of a Python app, our Django development services team wires the redirects, HSTS and secure-cookie settings into the application layer too.
Frequently Asked Questions
Is Let's Encrypt really free?
Yes. Let's Encrypt is run by the non-profit ISRG and charges nothing for its certificates; there is no paid tier and no per-domain fee. Your only costs are your own server, domain registration and DNS. The certificates are DV (Domain Validated) — if you need OV or EV certificates with organisation vetting, you'd buy those from a commercial CA instead.
How often do Let's Encrypt certificates need to be renewed?
Every 90 days. In practice you never renew by hand: Certbot's systemd timer runs twice daily and renews automatically once a certificate is within 30 days of expiry. The industry is moving toward even shorter lifetimes (a 47-day maximum by 2029), which only makes automated renewal more important.
How do I get a wildcard SSL certificate?
Use the DNS-01 challenge — it is the only method that can issue a wildcard such as *.example.com. HTTP-01 cannot. For unattended wildcard renewal, install a Certbot DNS plugin for your provider (for example certbot-dns-cloudflare or certbot-dns-route53) so the validation TXT record is created and removed automatically.
Do I still need a cron job for renewal?
No. Older tutorials hard-code a cron entry, but modern Certbot installs a systemd timer (or the snap's built-in timer) that handles renewal for you. Confirm it with systemctl list-timers | grep certbot and validate the whole flow with sudo certbot renew --dry-run.
Which TLS versions and protocols should I enable?
Enable TLS 1.2 and TLS 1.3 only, and disable TLS 1.0 and 1.1, which are deprecated. Generate the matching cipher list from the Mozilla SSL Configuration Generator using the "intermediate" profile, and add an HSTS header. Note that OCSP stapling is no longer needed with Let's Encrypt, which has moved from OCSP to CRLs.
What are Let's Encrypt's rate limits?
The main limits are 50 certificates per registered domain per week and 5 duplicate (identical) certificates per week, with separate caps on failed validations. While developing or scripting, point Certbot at the staging environment or use --dry-run so you don't exhaust the production limits on a misconfigured setup.