A Certbot webroot challenge is useful when an existing public HTTP site must keep serving traffic while ACME validation runs. Certbot writes a temporary challenge file below the site's document root, and the certificate authority must retrieve that exact file through the requested hostname on port 80.

The webroot plugin is an authenticator, not an installer. It can obtain certificate files under /etc/letsencrypt/live without stopping the web server or editing Nginx or Apache, but the web server configuration still needs to serve the challenge path during issuance and use the resulting certificate paths afterward.

The examples use one webroot for site.example.net and www.site.example.net. When hostnames use different document roots, place the matching -w option before the -d names that belong to that root, and use a DNS-01 challenge instead for wildcard names, private-only services, or sites where port 80 cannot be reached from the public Internet.

Steps to use a Certbot webroot challenge:

  1. Confirm that each hostname resolves to the server that serves the HTTP site.
    $ getent ahosts site.example.net
    203.0.113.10    STREAM site.example.net
    203.0.113.10    DGRAM
    203.0.113.10    RAW

    If the name also has an .AAAA record, that IPv6 address must serve the same challenge path or HTTP-01 validation can fail over IPv6.

  2. Confirm that the site answers over plain HTTP on port 80.
    $ curl -I http://site.example.net/
    HTTP/1.1 200 OK
    Server: nginx/1.28.0
    ##### snipped #####

    HTTP-01 starts on port 80. Redirects can work when they preserve the challenge URL and stay on ports 80 or 443, but redirects to login pages, other hosts, or blocked hidden directories will fail validation.

  3. Identify the document root used by the active virtual host.
    $ sudo nginx -T
    nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
    nginx: configuration file /etc/nginx/nginx.conf test is successful
    # configuration file /etc/nginx/sites-enabled/site.example.net:
    server {
        listen 80;
        server_name site.example.net www.site.example.net;
        root /var/www/site.example.net;
    }
    ##### snipped #####

    Use the directory served by the matching virtual host. On Apache, this is usually the matching DocumentRoot instead of the Nginx root directive.

  4. Create the challenge directory inside the webroot.
    $ sudo mkdir -p /var/www/site.example.net/.well-known/acme-challenge

    Certbot can create its own challenge files, but creating the directory first exposes hidden-directory and permission problems before the ACME request.

  5. Write a probe file into the challenge directory.
    $ printf 'challenge-ok\n' | sudo tee /var/www/site.example.net/.well-known/acme-challenge/probe-token
    challenge-ok
  6. Request the probe file through the public challenge URL.
    $ curl http://site.example.net/.well-known/acme-challenge/probe-token
    challenge-ok

    The body should be only the probe text. Repeat the check from outside the origin network when a load balancer, CDN, WAF, or separate IPv6 listener can change the route.
    Tool: ACME HTTP 01 Readiness Validator

  7. Confirm that Certbot has the webroot authenticator.
    $ sudo certbot plugins
    Saving debug log to /var/log/letsencrypt/letsencrypt.log
    ##### snipped #####
    * webroot
    Description: Saves the necessary validation files to a
    .well-known/acme-challenge/ directory within the nominated webroot path. A
    separate HTTP server must be running and serving files from the webroot path.
    HTTP challenge only (wildcards not supported).
    Interfaces: Authenticator, Plugin
    ##### snipped #####
  8. Rehearse the certificate request with the staging service.
    $ sudo certbot certonly --webroot -w /var/www/site.example.net -d site.example.net -d www.site.example.net -m admin@example.net --agree-tos --non-interactive --dry-run
    Saving debug log to /var/log/letsencrypt/letsencrypt.log
    Simulating a certificate request for site.example.net and www.site.example.net
    The dry run was successful.

    For domains served from different document roots, place the matching -w option before the -d names that use that webroot. --dry-run uses the staging service and does not save a production certificate.

  9. Request the production certificate after the dry run succeeds.
    $ sudo certbot certonly --webroot -w /var/www/site.example.net -d site.example.net -d www.site.example.net -m admin@example.net --agree-tos --non-interactive
    Saving debug log to /var/log/letsencrypt/letsencrypt.log
    Requesting a certificate for site.example.net and www.site.example.net
    
    Successfully received certificate.
    Certificate is saved at: /etc/letsencrypt/live/site.example.net/fullchain.pem
    Key is saved at:         /etc/letsencrypt/live/site.example.net/privkey.pem

    This command obtains the certificate only. Install /etc/letsencrypt/live/site.example.net/fullchain.pem and /etc/letsencrypt/live/site.example.net/privkey.pem in the web server configuration before expecting HTTPS traffic to use the new certificate.

  10. Confirm that Certbot recorded the certificate with the expected names.
    $ sudo certbot certificates --cert-name site.example.net
    Saving debug log to /var/log/letsencrypt/letsencrypt.log
    Found the following certs:
      Certificate Name: site.example.net
        Domains: site.example.net www.site.example.net
        Expiry Date: 2026-09-10 12:00:00+00:00 (VALID: 89 days)
        Certificate Path: /etc/letsencrypt/live/site.example.net/fullchain.pem
        Private Key Path: /etc/letsencrypt/live/site.example.net/privkey.pem
  11. Test renewal with the saved webroot configuration.
    $ sudo certbot renew --cert-name site.example.net --dry-run
    Saving debug log to /var/log/letsencrypt/letsencrypt.log
    Processing /etc/letsencrypt/renewal/site.example.net.conf
    Congratulations, all simulated renewals succeeded:
      /etc/letsencrypt/live/site.example.net/fullchain.pem (success)

    A successful renewal dry run confirms that Certbot can reuse the saved webroot authenticator and challenge path later.
    Related: Test Certbot certificate renewal
    Related: Check the Certbot renewal timer

  12. Remove the temporary probe file.
    $ sudo rm /var/www/site.example.net/.well-known/acme-challenge/probe-token

    Certbot removes its own ACME challenge files after validation. Removing the manual probe keeps the challenge directory from serving stale test content.