JWS has an invalid anti-replay nonce when client behind NAT


My domain is: insee.sanger.ac.uk

I ran this command: dehydrated -c -x

It produced this output:

HTTP/1.0 200 Connection established

HTTP/1.1 100 Continue
Expires: Wed, 11 Jul 2018 15:58:20 GMT
Cache-Control: max-age=0, no-cache, no-store
Pragma: no-cache

HTTP/1.1 400 Bad Request
Server: nginx
Content-Type: application/problem+json
Content-Length: 169
Boulder-Requester: 6437202
Replay-Nonce: Wk4kKi6k9_G2wPiyUxT9VM4ght0O8PBdNYaF_p0p1t8
Expires: Wed, 11 Jul 2018 15:58:20 GMT
Cache-Control: max-age=0, no-cache, no-store
Pragma: no-cache
Date: Wed, 11 Jul 2018 15:58:20 GMT
Connection: close

“type”: “urn:ietf:params:acme:error:badNonce”,
“detail”: "JWS has an invalid anti-replay nonce: “n6hlVrPXGmkIxODJD3Heu2BKeAEwieVhdC6UUacoeE4"”,
“status”: 400

My web server is (include version): civetweb (from ceph), but this is the DNS-validation protocol

The operating system my web server runs on is (include version): Ubuntu 16.04

I can login to a root shell on my machine (yes or no, or I don’t know): yes

I’m using a control panel to manage my site (no, or provide the name and version of the control panel): no


I’m using the DNS-challenge based approach, and I think this is a problem because I’m behind NAT (with many different IP addresses) - I see @jsha saying here that this can be an issue. Is there a workaround for where my client is behind NAT? Even retrying repeatedly doesn’t help, and if I do the same from my workstation rather than the server, then I’m always NATed to the same address and don’t see this problem. But for production, we want to run the dehydrated client on the production machines, which are behind multi-IP NAT; this is unlikely to be a very unusual config, so I’m bit surprised that letsencypt has a problem with it…




Boulder still does not share the nonce pool between instances of itself, and (appears to) rely on source IP stickyness to ensure your requests go to the same Boulder server.

So if you are making outgoing requests using different IPs, there’s a chance that your connections will go to different Boulder instances.

Unless you can configure outgoing sticky NAT, you might need to proxy your requests via an external host.


@_az is correct.

dehydrated is a shell script that runs a separate curl command for each request, which means each request is made on a new connection, which means you get assigned a new source IP address. Most likely a client that reuses an HTTPS connection over multiple requests would have much less trouble. That rules out shell-based clients, but other clients should work reasonably well.


Thanks for clarifying; perhaps this could be more clearly flagged in the
FAQ or similar?