Skip to content
DevOps devops domains 6 min read

Securing Nginx with Certbot

When you visit a site over HTTPS (the secure version of HTTP, the protocol browsers use to load web pages), the little padlock in the address bar means the connection is encrypted. That encryption needs an SSL/TLS certificate (a small file that proves your domain is who it says it is and lets browsers set up an encrypted connection). Certbot is a free command-line tool that gets one of these certificates from Let’s Encrypt (a non-profit that issues them for free) and wires it into your web server automatically. This page walks through using Certbot to add HTTPS to an Nginx site (Nginx is a popular, fast web server) on Ubuntu, step by step.

When to use Certbot with Nginx

Use Certbot’s Nginx plugin when Nginx is the server that terminates traffic for your domain (the public-facing server users connect to) and you want it to handle HTTPS directly. This is the most common setup for a website, a single-page app, or a reverse proxy (a server that sits in front of your app and forwards requests to it).

Do not use the Nginx plugin if HTTPS is already handled somewhere in front of Nginx — for example a cloud load balancer, Cloudflare in “Full” proxy mode, or another gateway that already presents the certificate. In those cases the edge handles the certificate and your Nginx box may not even be reachable on port 443 from the public internet. If you run Apache instead of Nginx, see the Certbot + Apache page.

SituationUse Certbot —nginx?
Nginx is your public web server / reverse proxyYes
A load balancer or Cloudflare terminates TLSNo — certificate lives at the edge
You only want the certificate file, not config editsUse certbot certonly instead
You need a wildcard cert (*.example.com)Not with this plugin — needs DNS validation

Before you start

Two things must already be true, or Certbot will fail:

  1. Your domain’s DNS A record (the record that maps a name like example.com to your server’s IP address) points at this server. Check it before anything else.
  2. Nginx already serves the site on port 80 (plain HTTP) and your firewall allows web traffic.
# Confirm DNS points here (compare the IP it prints to your server's IP)
dig +short example.com

# Confirm Nginx is running
sudo systemctl status nginx

Output:

203.0.113.42

● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; preset: enabled)
     Active: active (running) since Mon 2026-06-15 09:12:04 UTC; 2h ago

Make sure your UFW firewall (Uncomplicated Firewall, Ubuntu’s simple firewall front-end) lets in HTTP and HTTPS. The Nginx Full profile opens both ports 80 and 443:

sudo ufw allow 'Nginx Full'
sudo ufw status

Output:

Status: active

To                         Action      From
--                         ------      ----
Nginx Full                 ALLOW       Anywhere

Install Certbot

The Certbot project recommends installing via snap (Ubuntu’s universal package format) because it always ships the latest version. The older apt install certbot packages can lag behind.

# Make sure snapd is present and up to date
sudo apt update
sudo snap install core
sudo snap refresh core

# Install certbot and link it so you can run `certbot` directly
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot

Output:

certbot 3.1.0 from Certbot Effort (certbot-eff✓) installed

If you previously installed Certbot from apt, remove it first with sudo apt remove certbot so the two versions don’t clash on your PATH.

The snap of Certbot bundles the Nginx plugin, so there is nothing extra to install. (If you instead use the apt route, you would also need sudo apt install python3-certbot-nginx to get the plugin.)

Run Certbot for Nginx

Now the main event. The --nginx flag tells Certbot to read your Nginx config, find the domains, obtain a certificate, and edit the config to use it.

sudo certbot --nginx

Certbot is interactive the first time. It asks for an email (used for expiry warnings), asks you to agree to the terms, then lists the domains it found in your Nginx server blocks. Pick the one(s) you want.

Output:

Saving debug log to /var/log/letsencrypt/letsencrypt.log

Which names would you like to activate HTTPS for?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: example.com
2: www.example.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate numbers separated by commas and/or spaces, or leave input
blank to select all options shown (Enter 'c' to cancel): 1,2

Requesting a certificate for example.com and www.example.com

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/example.com/privkey.pem
This certificate expires on 2026-09-13.

Deploying certificate
Successfully deployed certificate for example.com to /etc/nginx/sites-enabled/example.com
Successfully deployed certificate for www.example.com to /etc/nginx/sites-enabled/example.com
Congratulations! You have successfully enabled HTTPS on https://example.com and
https://www.example.com

You can skip the prompts by passing everything on the command line, which is handy for scripts:

sudo certbot --nginx -d example.com -d www.example.com \
  --redirect --agree-tos -m [email protected] --no-eff-email

The --redirect flag tells Certbot to add a rule that automatically sends all plain HTTP visitors to HTTPS. If you leave it off, Certbot asks interactively whether to redirect — always say yes for a public site.

What Certbot changed in your config

Certbot edits your server block in place. After it runs, /etc/nginx/sites-available/example.com gains the certificate lines and a redirect block. The Certbot-added lines are marked with # managed by Certbot comments — leave those alone so future renewals can update them.

server {
    server_name example.com www.example.com;

    root /var/www/example.com/html;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
    if ($host = www.example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    if ($host = example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    listen 80;
    server_name example.com www.example.com;
    return 404; # managed by Certbot
}

The first block now listens on port 443 with TLS. The second block listens on port 80 and 301-redirects (a permanent “moved” redirect that browsers and search engines remember) every plain-HTTP request to the HTTPS version.

Verify HTTPS works

Reload Nginx and test the live site. Certbot already reloaded it, but it’s good practice to confirm the config is valid yourself.

sudo nginx -t
sudo systemctl reload nginx

# Follow redirects and inspect the TLS certificate
curl -sI http://example.com | head -n 1
curl -svo /dev/null https://example.com 2>&1 | grep -E 'subject:|expire'

Output:

nginx: configuration file /etc/nginx/nginx.conf test is successful

HTTP/1.1 301 Moved Permanently
*  subject: CN=example.com
*  expire date: Sep 13 06:00:00 2026 GMT

The 301 Moved Permanently confirms the HTTP-to-HTTPS redirect, and the certificate subject confirms the TLS handshake works. Open the site in a browser and you should see the padlock.

Certificates from Let’s Encrypt last only 90 days on purpose. Certbot installs a systemd timer that renews them automatically — verify it with sudo systemctl list-timers | grep certbot. Never plan to renew by hand. See the auto-renew page for details.

Best practices

  • Always confirm DNS points at the server before running Certbot — a wrong A record is the number-one cause of failed validation.
  • Use the snap install so you stay on a current, supported Certbot release with the latest TLS defaults.
  • Always enable the --redirect option so no traffic is ever served unencrypted.
  • Never hand-edit the # managed by Certbot lines; let Certbot own them so renewals apply cleanly.
  • Test renewals safely with sudo certbot renew --dry-run after setup to prove the automatic renewal will succeed.
  • Keep both the bare domain and the www subdomain on the same certificate so either name works with HTTPS.
Last updated June 15, 2026
Was this helpful?