Certbot fails using haproxy

Certbot fails to update it's cert. I suspect it can't read the acme-challenge file. But the log doesn't give me enough info as to why it fails?

My domain is: logic-q.nl

I ran this command:
certbot certonly --manual -d iot.logic-q.com

It produced this output:

Requesting a certificate for iot.logic-q.com

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Create a file containing just this data:

becuOmv-**snip**-PBepXonrg.**snip**-kY5p9AM

And make it available on your web server at this URL:

http://iot.logic-q.com/.well-known/acme-challenge/becuOmv-**snip**-PBepXonrg

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue

Certbot failed to authenticate some domains (authenticator: manual). The Certificate Authority reported these problems:
  Domain: iot.logic-q.com
  Type:   unauthorized
  Detail: 88.159.21.27: Invalid response from https://iot.logic-q.com/.well-known/acme-challenge/becuOmv-QAF6w_i9QTNmLRPN1IrsHgXlx-PBepXonrg: 503

Hint: The Certificate Authority failed to verify the manually created challenge files. Ensure that you created these in the correct location.

My web server is (include version): certbot standalone
The operating system my web server runs on is (include version): Ubuntu 22.04.5
The version of my client is (e.g. output of certbot --version or certbot-auto --version if you're using Certbot): certbot 1.21.0

haproxy.conf

frontend haproxy
        bind *:80
        bind *:443 ssl crt /etc/haproxy/certs/mydomains.pem alpn h2,http/1.1
        mode http

        redirect scheme https if !{ ssl_fc }

        acl letsencrypt_path path_beg /.well-known/acme-challenge/
        use_backend letsencrypt-backend if letsencrypt_path
backend letsencrypt-backend
        mode http
        server letsencrypt 192.168.2.12:80 check

letsencrypt.log

2025-03-27 00:18:16,206:DEBUG:acme.client:Received response:
HTTP 200
Server: nginx
Date: Wed, 26 Mar 2025 23:18:16 GMT
Content-Type: application/json
Content-Length: 1344
Connection: keep-alive
Boulder-Requester: 18162
Cache-Control: public, max-age=0, no-cache
Link: <https://acme-v02.api.letsencrypt.org/directory>;rel="index"
Replay-Nonce: Gsl22hvlL1AbISrwGhwEbc59yam0QbUzWJX8UgArqrAeGM6cBDA
X-Frame-Options: DENY
Strict-Transport-Security: max-age=604800

{
  "identifier": {
    "type": "dns",
    "value": "iot.logic-q.com"
  },
  "status": "invalid",
  "expires": "2025-04-02T23:17:48Z",
  "challenges": [
    {
      "type": "http-01",
      "url": "https://acme-v02.api.letsencrypt.org/acme/chall/18162/495949428326/X7BYIA",
      "status": "invalid",
      "validated": "2025-03-26T23:18:11Z",
      "error": {
        "type": "urn:ietf:params:acme:error:unauthorized",
        "detail": "88.159.21.27: Invalid response from https://iot.logic-q.com/.well-known/acme-challenge/becuOmv-QAF6w_i9QTNmLRPN1IrsHgXlx-PBepXonrg: 503",
        "status": 403
      },
      "token": "becuOmv-QAF6w_i9QTNmLRPN1IrsHgXlx-PBepXonrg",
      "validationRecord": [
        {
          "url": "http://iot.logic-q.com/.well-known/acme-challenge/becuOmv-QAF6w_i9QTNmLRPN1IrsHgXlx-PBepXonrg",
          "hostname": "iot.logic-q.com",
          "port": "80",
          "addressesResolved": [
            "88.159.21.27"
          ],
          "addressUsed": "88.159.21.27"
        },
        {
          "url": "https://iot.logic-q.com/.well-known/acme-challenge/becuOmv-QAF6w_i9QTNmLRPN1IrsHgXlx-PBepXonrg",
          "hostname": "iot.logic-q.com",
          "port": "443",
          "addressesResolved": [
            "88.159.21.27"
          ],
          "addressUsed": "88.159.21.27"
        }
      ]
    }
  ]
}

Your server responded with a 503 error code.

Before you "Press Enter to Continue" make sure you can access the file via http://iot.logic-q.com/.well-known/acme-challenge/becuOmv-**snip**-PBepXonrg (or whatever URL it tells you to place the file at)

The 503 error you see may give more details.

3 Likes

I think you'll need to run certbot with the --standalone flag.

From what I understand, you have HAProxy on a separate server from the one where you're running certbot, and HAProxy is forwarding only requests to the .well-known/acme-challenge path to 192.168.2.12:80. But if there's no HTTP server running at that address, that's probably why the challenge is failing.

So I’d assume you need to start Certbot with the --standalone flag.

1 Like

I had multiple subdomain backends in haproxy.conf. Because they where placed above the letsencrypt rules, haproxy redirected requests to those domains to the wrong backend server.

Placing letsencrypt rules above the other acl and use_backend rules solved it partly. The last trick was to redirect letsencrypt traffic to use http (port 80) and all other traffic to use https (443).

        # redirect scheme https if !{ ssl_fc }
        redirect scheme https if !letsencrypt_path !{ ssl_fc }

I don't know if certbot renew still works if the https cert becomes invalid for some reason. So that's why I redirect letsencrypt trafic to http.

3 Likes

Hi @Hansaplast,

The HTTP-01 challenge states "When redirected to an HTTPS URL, it does not validate certificates (since this challenge is intended to bootstrap valid certificates, it may encounter self-signed or expired certificates along the way)."

2 Likes