Serve a site with nginx and HTTPS
Install nginx, point a domain at your server, and get a free Let's Encrypt certificate that renews itself, on Ubuntu 24.04 or Debian 12.
By the end of this, example.com serves a page over HTTPS with a free certificate that renews itself. Swap in your own domain, and your server's addresses for 203.0.113.42 and 2001:db8:9::2.
Point the domain at the server
DNS first, because it can take a few minutes to show up and certbot needs it later. At your DNS provider, add:
example.com. A 203.0.113.42
example.com. AAAA 2001:db8:9::2
www.example.com. A 203.0.113.42
www.example.com. AAAA 2001:db8:9::2Only add the AAAA records once IPv6 actually works on the server (Check that IPv6 works shows how). A broken AAAA record is the most common reason certbot fails later.
Check from your computer:
dig +short example.com A
dig +short example.com AAAABoth should print your server's addresses.
Install nginx
apt update
apt install -y nginxIf you use ufw, open HTTP and HTTPS. The Nginx Full profile covers 80 and 443:
ufw allow 'Nginx Full'Browse to http://203.0.113.42 and you should see the nginx welcome page.
Add your site
Make a folder and a test page:
mkdir -p /var/www/example.com
echo '<h1>It works</h1>' > /var/www/example.com/index.htmlThen create /etc/nginx/sites-available/example.com:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example.com;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}Switch it on, test the config, reload:
ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginxhttp://example.com should say "It works".
Get the certificate
Install certbot and its nginx plugin from the distribution:
apt install -y certbot python3-certbot-nginxThen ask for the certificate:
certbot --nginx -d example.com -d www.example.comIt asks for an email address (for expiry warnings) and for you to agree to the Let's Encrypt terms. Then it proves you control the domain over port 80, saves the certificate, and edits your nginx file to serve HTTPS and redirect plain HTTP. All of that takes about a minute.
Check it:
curl -I https://example.comThe first line should be HTTP/2 200 or HTTP/1.1 200 OK.
Renewals
Certificates last 90 days, and the certbot package installs a systemd timer that renews them for you. It's worth confirming that once rather than finding out in three months:
systemctl list-timers certbot.timer
certbot renew --dry-runThe dry run should finish with Congratulations, all simulated renewals succeeded.
If it fails
nginx: [emerg]fromnginx -t: the message names the file and line. Nine times out of ten it's a missing;.- Certbot says
Timeout during connectorConnection refused: Let's Encrypt can't reach port 80. Checkufw statusallowsNginx Full, and that the A record points here. - It fails only over IPv6: the
AAAArecord points at an address that doesn't answer. Fix IPv6, or remove theAAAArecord and try again. - "Too many failed authorizations": Let's Encrypt rate-limits failures. Practise against staging with
certbot certonly --nginx --dry-run -d example.com -d www.example.comuntil it passes, then run the real command.