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.

StateWhat changesRisk
Domain added to serverNginx server block listens on port 80DNS not pointing here fails HTTP-01
Certificate requestedCertbot edits server block, adds TLS directivesPort 80 redirect already in place breaks challenge
Site live on HTTPSClients connect on 443Default server block can shadow the right certificate
Renewal runsSame challenge path re-executedNginx 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 domainThe 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 challengeA 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 installA 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 validatedCertbot 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 exceededLet’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 editCertbot 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 downA 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 validityNinety days. Let’s Encrypt documents this at letsencrypt.org/docs/frequently-asked-questions. The renewal timer runs twice a day.
Port 80 requirementThe HTTP-01 challenge always runs over port 80. Closing it after issuance breaks the next renewal.
Wildcard certificatesWildcard 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 backRun certbot rollback to revert the last Nginx configuration edit Certbot made. Manual edits stay in place.
Moving to a new serverThe certificate files are in /etc/letsencrypt/live/example.com/. Issuing a fresh certificate on the new server is cleaner than moving the old one.
Pankaj Kumar
Pankaj Kumar

Pankaj Kumar is the founder and CEO of CodeForGeek, with more than 14 years in IT. He is an open-source enthusiast who enjoys sharing what he learns through CodeForGeek and YouTube, with a focus on Python, data analytics, machine learning, Angular, Node.js, and Kafka.

Articles: 337