HTTPS / TLS

Serve the Let's Encrypt ACME challenge

3 min · updated June 16, 2026

Let’s Encrypt’s HTTP-01 challenge works by fetching a token from http://yourdomain/.well-known/acme-challenge/<token>. That one path must stay reachable over plain HTTP, even though you redirect everything else to HTTPS.

Port-80 block that serves the challenge and redirects the rest:

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

    # ACME challenge: served over HTTP from a shared webroot.
    location ^~ /.well-known/acme-challenge/ {
        root /var/www/certbot;
        default_type "text/plain";
    }

    # Everything else -> HTTPS.
    location / {
        return 301 https://$host$request_uri;
    }
}

The ^~ modifier matters: it tells Nginx that once this prefix matches, don’t fall through to the redirect — otherwise the challenge request would get 301’d and validation fails.

Issue the certificate in webroot mode (writes the token into that same directory):

sudo mkdir -p /var/www/certbot
sudo certbot certonly --webroot -w /var/www/certbot \
  -d example.com -d www.example.com

Then point your HTTPS block at the issued files (see the TLS recipe):

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

Renewal:

sudo certbot renew --deploy-hook "nginx -s reload"
sudo nginx -t && sudo nginx -s reload

← All snippets