# unauthorized Invalid response 403：Certbot 验证被拒的修法

> Let's Encrypt 连上了服务器，但取验证文件时被返回 403 Forbidden。多是禁止访问点开头路径的规则、WAF 或文件权限导致，放行 /.well-known/acme-challenge/ 后重试。

Source: https://ssl.sh/zh/errors/certbot-unauthorized-403/
Last updated: 2026-11-10

**这个报错的意思是：Let's Encrypt 连上了你的 Web 服务器 80 端口，请求 `/.well-known/acme-challenge/` 下的验证文件，得到的却是 403 Forbidden。** Web 服务器内部或前面有东西拒绝了这个路径：禁止访问点开头文件的规则、WAF 或 CDN、文件权限。

## 快速修复

修法是给 `/.well-known/acme-challenge/` 加一个优先于其他规则的放行例外。先从外部网络模拟一次请求：

```bash
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](https://ssl.sh/zh/errors/certbot-timeout-during-connect/)。Nginx 写法：

```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 写法：

```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](https://ssl.sh/zh/glossary/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 速率限制](https://ssl.sh/zh/errors/lets-encrypt-rate-limit/)。
- **把 WAF 整个关掉。** 只豁免验证路径，或者改用 DNS-01。
