New to Rust? Grab our free Rust for Beginners eBook Get it free →
How to setup SSL in Nginx Server with LetsEncrypt

Certbot’s HTTP-01 challenge only works through port 80, and renewal fails when the server block or the port changes after the first issuance. A certificate on disk does not prove HTTPS works.
I tested a workspace nginx -t configuration and it passed. I ran a live certificate check against codeforgeek.com, which returned a Google Trust Services issuer. The old PPA installation path no longer matches what you get from a fresh Ubuntu install.
How Certbot secures an Nginx server
Let’s Encrypt issues certificates through an ACME challenge. The Certbot Nginx plugin uses HTTP-01 by default, which serves a token over port 80 to prove you control the domain. Certbot modifies your Nginx server block to add the ssl_certificate and ssl_certificate_key directives and typically redirects HTTP to HTTPS.
Renewal runs from a systemd timer or a snap timer and re-runs the same challenge. The catch is that renewal reproduces whatever state Nginx had at issuance time. A server block edit or a closed port 80 breaks the renewal before it starts.
| State | What changes | Risk |
|---|---|---|
| Domain added to server | Nginx server block listens on port 80 | DNS not pointing here fails HTTP-01 |
| Certificate requested | Certbot edits server block, adds TLS directives | Port 80 redirect already in place breaks challenge |
| Site live on HTTPS | Clients connect on 443 | Default server block can shadow the right certificate |
| Renewal runs | Same challenge path re-executed | Nginx edits since issuance break renewal |
What you need before requesting the certificate
- A domain whose A or AAAA record reaches this server’s public IP
- Port 80 open to the internet, not just the local network
- An Nginx server block that listens on port 80 and matches the domain name
- A backup of the current Nginx configuration
Run a preflight check on the domain and the port. The first command should return your server’s IP. The second should reach port 80 from outside.
dig +short example.com
curl -I --max-time 5 http://example.com/.well-known/acme-challenge/test
If either fails, fix the DNS record or the firewall before running Certbot. The Let’s Encrypt HTTP-01 challenge does not traverse redirects and does not run on any port except 80. The installation path has changed since the original Certbot PPA days, so the snap package is now the recommended approach.
How to configure Let’s Encrypt SSL in Nginx
The installation path has changed since the original Certbot PPA days. The current recommendation is the snap package, which tracks the latest release and refreshes automatically.
Check Nginx before changing it
sudo nginx -t
That command parses the configuration and every file it references. If it fails, fix the existing configuration before adding Certbot to the mix.
Install the current Certbot
The Certbot team now recommends the snap installation on Ubuntu. The apt packages exist but lag behind. Install it with snap, link the binary to /usr/bin, and confirm the version.
sudo snap install --classic certbot
sudo ln -sf /snap/bin/certbot /usr/bin/certbot
certbot --version
Request the certificate
Run the Certbot Nginx plugin against your domain. Certbot authenticates over port 80, writes the certificate files under /etc/letsencrypt/live/example.com/, and edits the Nginx server block to add the TLS directives.
When the prompt asks whether to redirect HTTP to HTTPS, choose option 2. A redirect without the HTTPS server block already in place drops the request.
Verify the live endpoint
Check that Nginx serves the new certificate. If you see a self-signed issuer or the wrong domain, the server block is serving a default certificate instead of the Let’s Encrypt one.
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -noout -issuer -dates
Fix Certbot and Nginx certificate errors
| Challenge failed for a single domain | The A or AAAA record for that subdomain does not point here. Check each -d argument resolves to this IP before running Certbot. |
| Port 80 refused during challenge | A firewall, a cloud security group, or a redirect from 80 to 443 before Certbot runs. Disable the redirect during issuance and re-enable it after. |
| Wrong certificate served after install | A default server block is matching the request. Set default_server explicitly on the HTTP block, or add server_name to the HTTPS block so it wins. |
| IPv6 address not validated | Certbot requests a certificate for every resolved address. If the AAAA record exists but Nginx does not listen on [::]:80, the challenge fails. Remove the AAAA record or add the IPv6 listen directive. |
| Rate limit exceeded | Let’s Encrypt allows five duplicate certificate requests per week. If you exceed that limit, wait or add a variant domain. |
| nginx -t fails after Certbot edit | Certbot added a directive inside the wrong block. Look for ssl_certificate outside a server {} block, move it, and run sudo nginx -t. |
| Certbot renew returns no errors but the site went down | A manual Nginx edit changed the server name or the document root after issuance. Run sudo nginx -t after every configuration change. |
Prove renewal before relying on it
The –dry-run flag exercises the full renewal path without writing a certificate or counting against the rate limit. A successful dry run confirms the timer exists, port 80 is reachable, and the current Nginx configuration still satisfies the authentication path Certbot recorded at issuance.
sudo certbot renew --dry-run
If the dry run fails, the timer or the port has drifted since the certificate was issued. The service that runs renewal on this snap install is sudo snap services certbot.renew. Renewal only succeeds when the state at renewal time matches the state at issuance time.
Certbot and Nginx FAQ
| Certificate validity | Ninety days. Let’s Encrypt documents this at letsencrypt.org/docs/frequently-asked-questions. The renewal timer runs twice a day. |
| Port 80 requirement | The HTTP-01 challenge always runs over port 80. Closing it after issuance breaks the next renewal. |
| Wildcard certificates | Wildcard issuance requires the DNS-01 challenge through a DNS TXT record. Use certbot certonly –manual –preferred-challenges dns, then point Nginx at the resulting files. |
| Rolling back | Run certbot rollback to revert the last Nginx configuration edit Certbot made. Manual edits stay in place. |
| Moving to a new server | The certificate files are in /etc/letsencrypt/live/example.com/. Issuing a fresh certificate on the new server is cleaner than moving the old one. |




