acme.sh fails with error 404 - ACME challenge not found (Nginx)

Recently I can into an issue with acme.sh / Let’s Encrypt and a failing ACME validation

Error 404 when running acme.sh --renew -d mydomain.tld

[Wed May  3 15:31:45 UTC 2023] Pending, The CA is processing your order, please just wait. (1/30)
[Wed May  3 15:31:49 UTC 2023] mydomain.tld:Verify error:<ipaddress> Invalid response from https://mydomain.tld/.well-known/acme-challenge/5GmSwd0P0ukTtX302yHHhAuZMCEDJx7MmAaBBoPIKtk: 404
[Wed May  3 15:31:49 UTC 2023] Please add '--debug' or '--log' to check more details.
[Wed May  3 15:31:49 UTC 2023] See: https://github.com/acmesh-official/acme.sh/wiki/How-to-debug-acme.sh

The problem:

Probably your Nginx config has two segments:

server {
	listen 80;
	listen [::]:80;

	...(some redirect to HTTPS)...
}

server {
	listen 443 ssl http2;
	listen [::]:443 ssl http2;

	..etc etc ... 
}

Cause of the error:

acme.sh will only insert its temporary acme-related redirection snippet into the first “server” section but not in the second one. Still it will try to connect to the server via HTTPS, first. There will be no ACME redirection (via temporary inserted “location” block), so the ACME server will not find the challenge file (thus: Error 404).

Solution:

Put everything into one server block, like this:

server {
	listen 80;
	listen [::]:80;
	listen 443 ssl http2;
	listen [::]:443 ssl http2;

	# HTTP redirect
	if ($scheme = http) {
			return 301 https://$server_name$request_uri;
	}
}

Then:

systemctl reload nginx

And try again:

acme.sh --renew -d mydomain.tld
If the renewal keeps failing and you want to try different configurations, make sure to use the “staging” API of Letsencrypt in the meantime. The main API is rate-limited and will block you after a few tries per hour. Enable the staging API by using the --staging flag. If the renewal finally works, go back to the production API to receive a production certificate, e.g. acme.sh --renew --force -d mydomain.tld.

=> Profit :-)