Issuance and renewal
Certbot "unauthorized ... Invalid response: 403" fix
Type: unauthorized Detail: 203.0.113.10: Invalid response from http://example.com/.well-known/acme-challenge/...: 403This error means Let's Encrypt reached your web server on port 80, requested the HTTP-01 challenge file under /.well-known/acme-challenge/, and was answered with 403 Forbidden. Something in front of or inside the web server refuses that path: a "deny dotfiles" rule, a WAF or CDN, or file permissions.
Quick fix
An explicit allow rule for /.well-known/acme-challenge/ fixes most 403 failures.
Reproduce the request yourself, from outside your network:
sudo mkdir -p /var/www/html/.well-known/acme-challenge
echo ok | sudo tee /var/www/html/.well-known/acme-challenge/test
curl -i http://example.com/.well-known/acme-challenge/test
If you also get 403, add an explicit exception before other rules. In Nginx:
location ^~ /.well-known/acme-challenge/ {
root /var/www/html;
default_type "text/plain";
allow all;
}
Reload (sudo nginx -t && sudo systemctl reload nginx), confirm curl returns ok, then run sudo certbot renew --dry-run.
Causes
A web server rule that denies paths starting with a dot is the most common cause.
1. A rule denies hidden paths
Many hardening snippets contain location ~ /\. { deny all; } (Nginx) or a <FilesMatch "^\."> / RedirectMatch 403 /\. rule (Apache). They block /.well-known/ too.
- Confirm:
curlabove returns 403 from your server (check theServer:header), and the rule exists innginx -Torapachectl -Soutput. - Fix: add the
^~ /.well-known/acme-challenge/location shown above (the^~modifier stops regex locations from matching). In Apache:
<Location "/.well-known/acme-challenge/">
Require all granted
</Location>
2. A WAF, CDN or security plugin blocks the request
Bot protection, country blocking and some CMS security plugins return 403 to unknown clients. Let's Encrypt validates from several network locations, so blocking any of them can fail validation.
- Confirm: the 403 page or
Server:header comes from the CDN or WAF, or the plugin's log shows the block. - Fix: add a rule that skips protection for
/.well-known/acme-challenge/*, or use DNS-01.
3. File system permissions or SELinux
- Confirm: the web server's error log shows
Permission deniedfor the challenge file; on RHEL-family systemsls -Zshows a label other thanhttpd_sys_content_t. - Fix: make the directory traversable and the file readable by the web server user (
chmod 755for directories,644for files), and runsudo restorecon -Rv /var/www.
4. The HTTPS side or the application returns 403
Your port 80 server redirects to HTTPS, and the HTTPS server block or the application (a framework router, an authentication layer) denies the path.
- Confirm:
curl -iLshows a 301 to HTTPS, then 403. - Fix: add the same exception to the HTTPS server block, or serve the challenge on port 80 before the redirect.
5. IP allowlists or basic auth on a staging site
Sites protected with allow/deny lists or HTTP basic authentication return 401 or 403 to the CA.
- Fix: exempt the challenge path, or use DNS-01 for non-public sites.
Fixes by environment
Every environment needs the same exception: /.well-known/acme-challenge/ must answer over plain HTTP without authentication or bot checks. acme.sh reports the same failure as a Verify error.
Certbot --nginx / --apache: the plugin adds a temporary rule, but a site-wide deny rule or an include processed earlier can still win; use --webroot with the explicit location above if it keeps failing.
Cloudflare and other proxies: disable "under attack" style challenges for the path or temporarily set the DNS record to DNS-only while validating.
WordPress and similar CMSs: some security plugins add .htaccess rules denying dot-paths; whitelist /.well-known/acme-challenge/.
Docker: make sure the container that answers port 80 shares the webroot volume Certbot writes to.
Don't do this
- Remove all dotfile protections. They stop leaks of
.git,.envand similar files. Add a narrow exception instead. - Retry repeatedly against production. Failed validations count toward the limit of 5 authorization failures per identifier per hour; see Let's Encrypt rate limits.
- Turn off the WAF entirely. Exempt only the challenge path, or switch to DNS-01.
FAQ
Does Let's Encrypt follow redirects to HTTPS?
Yes. HTTP-01 starts on port 80 but follows redirects to HTTP or HTTPS on ports 80 and 443. If the HTTPS side returns 403 for the challenge path, you get this error, so the exception must exist in both server blocks.
How can I test without using up retries?
Write a test file into the challenge directory and fetch it from outside. Then use certbot certonly --dry-run, which runs against the staging environment and does not count toward production limits.
What does 404 instead of 403 mean?
404 means the server allowed the request but did not find the file: the webroot passed to Certbot is not the directory the server serves for that name, or another server block answers. 403 means the request was refused.