# 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.

Source: https://vpsnine.com/help/nginx-and-https · Updated: 2026-10-09

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:

```text
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::2
```

Only add the `AAAA` records once IPv6 actually works on the server ([Check that IPv6 works](https://vpsnine.com/help/ipv6-setup) shows how). A broken `AAAA` record is the most common reason certbot fails later.

Check from your computer:

```bash
dig +short example.com A
dig +short example.com AAAA
```

Both should print your server's addresses.

## Install nginx

```bash
apt update
apt install -y nginx
```

If you use ufw, open HTTP and HTTPS. The `Nginx Full` profile covers 80 and 443:

```bash
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:

```bash
mkdir -p /var/www/example.com
echo '<h1>It works</h1>' > /var/www/example.com/index.html
```

Then create `/etc/nginx/sites-available/example.com`:

```nginx
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:

```bash
ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
```

`http://example.com` should say "It works".

## Get the certificate

Install certbot and its nginx plugin from the distribution:

```bash
apt install -y certbot python3-certbot-nginx
```

Then ask for the certificate:

```bash
certbot --nginx -d example.com -d www.example.com
```

It 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:

```bash
curl -I https://example.com
```

The 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:

```bash
systemctl list-timers certbot.timer
certbot renew --dry-run
```

The dry run should finish with `Congratulations, all simulated renewals succeeded`.

## If it fails

- `nginx: [emerg]` from `nginx -t`: the message names the file and line. Nine times out of ten it's a missing `;`.
- Certbot says `Timeout during connect` or `Connection refused`: Let's Encrypt can't reach port 80. Check `ufw status` allows `Nginx Full`, and that the A record points here.
- It fails only over IPv6: the `AAAA` record points at an address that doesn't answer. Fix IPv6, or remove the `AAAA` record 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.com` until it passes, then run the real command.
