Bug Report: ACME v1, unexpected 404 from API

Expected behavior:

Let’s Encrypt API doesn’t return a 404 when attempting to poll a completed challenge (e.g. https://acme-v01.api.letsencrypt.org/acme/authz/<challenge> )

Actual behavior:

404 urn:acme:error:malformed: No such challenge

Runtime environment:

Go’s golang.org/x/crypto acme.WaitAuthorization(

Additional info:

We’ve successfully used ACMEv1 for over a year and this change in behavior is correlated with the Boulder deploy on Thursday.

@jsha, I hope you don’t mind me pinging you. I know you’ve been interested in similar regressions in the past.

I don’t have a great understanding of the inner workings of Boulder, but I see from the changelog that a lot of it centered restricting certain functions to DNS identifier types.

jsha is away until some time next week apparently, but it looks like Let’s Encrypt recently re-enabled the authz2 feature flag in production. That change wouldn’t be visible in git but you can infer it from looking at new orders.

There was another thread related to authz2 recently, maybe you can find some useful info in there.


I don’t know much about Go ACME clients, but since no one else is talking…

Can you post logs and/or source code of what’s happening?

Was authzv2 enabled when this happened? (Note: Nothing to do with ACMEv2.)

Authzv2 URLs don’t match that – they are, for example, https://acme-v01.api.letsencrypt.org/acme/authz-v3/32100467.

Could the client be storing the URLs incorrectly?


(Also, challenges and authorizations are different things.)

Certbot is working for me with https://acme-v01.api.letsencrypt.org/.


The structure of authorization and challenge URLs is not guaranteed and shouldn’t be assumed by clients. As far as I am aware the x/crypto/acme package correctly derives authorization and challenge URLs from Location headers (and in order objects for the V2 API). If your client is directly constructing authorization or challenge URLs using their IDs it is likely to be quite brittle to any Boulder changes.


(is not guaranteed)

1 Like

Oops, yes! Thanks for catching this, will update my post!

Thank you everyone who responded. FWIW, our client implementation was attempting to convert challenge URLs to authz URLs, and it got tripped up on the change in the base path in authv2. Thanks for pointing me in the right direction!


Just in case you haven’t figured out a work-around already, there’s an RFC-8555 supported way of doing this (that works in ACME v1 as well) that doesn’t require manipulating URLs: Challenge objects are returned with a Link header with an "up" relation with the authorization’s URL.

Here’s an ACME v2 response to a GET for a challenge URL. The relevant headers would be the same for ACME v1:

HTTP/1.1 200 OK
Content-Length: 190
Cache-Control: max-age=0, no-cache, no-store
Connection: keep-alive
Content-Type: application/json
Date: Wed, 28 Aug 2019 14:01:57 GMT
Expires: Wed, 28 Aug 2019 14:01:57 GMT
Link: <https://acme-staging-v02.api.letsencrypt.org/directory>;rel="index"
Link: <https://acme-staging-v02.api.letsencrypt.org/acme/authz-v3/6187527>;rel="up"
Location: https://acme-staging-v02.api.letsencrypt.org/acme/chall-v3/6187527/WS-oZg
Pragma: no-cache
Server: nginx
Strict-Transport-Security: max-age=604800
X-Frame-Options: DENY

  "type": "http-01",
  "status": "pending",
  "url": "https://acme-staging-v02.api.letsencrypt.org/acme/chall-v3/6187527/WS-oZg",
  "token": "6vGa4oAFaJFnr4DjCPAVunt_6Z8q8-t2PVnZ65FUkv4"

The key bit is:

Link: <https://acme-staging-v02.api.letsencrypt.org/acme/authz-v3/6187527>;rel="up"
Location: https://acme-staging-v02.api.letsencrypt.org/acme/chall-v3/6187527/WS-oZg

The rel="up" link is the authz URL for the challenge identified in the Location.

Hope that helps!


This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.