证书签发和续期
unauthorized Invalid response 403:Certbot 验证被拒的修法
Type: unauthorized Detail: 203.0.113.10: Invalid response from http://example.com/.well-known/acme-challenge/...: 403这个报错的意思是:Let's Encrypt 连上了你的 Web 服务器 80 端口,请求 /.well-known/acme-challenge/ 下的验证文件,得到的却是 403 Forbidden。 Web 服务器内部或前面有东西拒绝了这个路径:禁止访问点开头文件的规则、WAF 或 CDN、文件权限。
快速修复
修法是给 /.well-known/acme-challenge/ 加一个优先于其他规则的放行例外。先从外部网络模拟一次请求:
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
拿到 403 就在其他规则之前加例外;curl 超时而不是 403,见 Timeout during connect。Nginx 写法:
location ^~ /.well-known/acme-challenge/ {
root /var/www/html;
default_type "text/plain";
allow all;
}
重载(sudo nginx -t && sudo systemctl reload nginx),确认 curl 返回 ok,再执行 sudo certbot renew --dry-run。
原因
403 有 5 种来源,最常见的是禁止访问点开头路径的规则把 /.well-known/ 也拦了。
1. 有规则禁止访问隐藏路径
很多安全加固配置里有 location ~ /\. { deny all; }(Nginx),或者 <FilesMatch "^\.">、RedirectMatch 403 /\.(Apache),它们会把 /.well-known/ 一起拦掉。
- 确认: 上面的
curl返回的 403 来自你的服务器(看Server:响应头),并且nginx -T或 Apache 配置里能找到这条规则。 - 修复: 加上面那段
^~ /.well-known/acme-challenge/(^~能阻止正则 location 再匹配)。Apache 写法:
<Location "/.well-known/acme-challenge/">
Require all granted
</Location>
2. WAF、CDN 或安全插件拦截
机器人防护、地域封禁、部分网站程序的安全插件,会对陌生客户端返回 403。Let's Encrypt 从多个境外网络位置验证,任何一个被拦都可能导致失败。
- 确认: 403 页面或
Server:头来自 CDN 或 WAF,或者插件日志里有拦截记录。 - 修复: 加一条规则,对
/.well-known/acme-challenge/*跳过防护;或改用 DNS-01。
3. 文件权限或 SELinux
- 确认: Web 服务器错误日志里有验证文件的
Permission denied;CentOS/Rocky 上ls -Z显示标签不是httpd_sys_content_t。 - 修复: 目录
chmod 755、文件644,让 Web 服务器用户能读到;执行sudo restorecon -Rv /var/www。
4. HTTPS 那边或应用程序返回 403
80 端口跳转到了 HTTPS,而 HTTPS 的 server 块或应用(框架路由、登录校验中间件)拒绝了这个路径。
- 确认:
curl -iL显示先 301 到 HTTPS,然后 403。 - 修复: 在 HTTPS 的 server 块里也加同样的例外,或者在 80 端口跳转之前就直接返回验证文件。
5. 测试站点加了 IP 白名单或密码保护
用 allow/deny 或 HTTP 基本认证保护的站点,会对 CA 返回 401 或 403。
- 修复: 对验证路径豁免,非公开站点干脆用 DNS-01。
各环境修法
所有环境只需对 /.well-known/acme-challenge/ 放行,其他防护规则保持不变。
宝塔面板: 宝塔申请证书时会自动处理验证目录,但如果开了“防跨站”之外的自定义规则、宝塔防火墙的 CC 防御或“禁止海外访问”,验证请求可能被拦。到防火墙日志里确认,对 /.well-known/acme-challenge/ 加白名单,或在申请方式里改选 DNS 验证。
阿里云 / 腾讯云 WAF、CDN: 在 WAF 里给验证路径加精准访问控制的放行规则;CDN 要把这个路径回源到服务器且不缓存。
Certbot --nginx / --apache: 插件会临时加规则,但全站的 deny 规则或更早处理的 include 仍可能生效;反复失败就改用 --webroot,配合上面的 location。
WordPress 等网站程序: 一些安全插件会在 .htaccess 里禁止点开头的路径,需要把 /.well-known/acme-challenge/ 加进白名单。
Docker: 响应 80 端口的容器要挂载 Certbot 写入的同一个 webroot 卷。
不要这样做
以下做法要么削弱网站防护,要么耗光验证次数。
- 删掉所有隐藏文件保护规则。 它们能防止
.git、.env等文件泄露,只加一个精确的例外就行。 - 对正式环境反复重试。 验证失败计入每小时 5 次的限制,见 Let's Encrypt 速率限制。
- 把 WAF 整个关掉。 只豁免验证路径,或者改用 DNS-01。
常见问题
Let's Encrypt 会跟随跳转到 HTTPS 吗?
会。HTTP-01 从 80 端口开始,但会跟随到 80 或 443 端口上的 HTTP/HTTPS 跳转。如果 HTTPS 那边对验证路径返回 403,也会出这个错,所以两个 server 块里都要放行。
怎么测试才不浪费重试次数?
往验证目录写一个测试文件,从外部访问它。然后用 certbot certonly --dry-run,它走的是测试环境,不计入正式环境的限制。
返回 404 而不是 403 是什么意思?
404 表示服务器允许了请求但找不到文件:传给 Certbot 的 webroot 不是这个域名实际使用的网站目录,或者是另一个 server 块在响应。403 则是请求被拒绝。
国内服务器、域名没备案,会是这个错吗?
有可能。国内云服务器上未备案的域名,80/443 的访问会被云厂商拦截,返回的是拦截提示页而不是你的验证文件,状态码因厂商而异,Let's Encrypt 会报 unauthorized。这种情况要先完成备案,或者改用 DNS-01 验证。