# AH01909：Apache 证书里没有和 ServerName 匹配的域名

> AH01909 是 Apache 启动时的警告：虚拟主机的 ServerName 不在证书的 SAN 或 CN 里。通常是没写 ServerName 或证书配错了，附排查命令。

Source: https://ssl.sh/zh/errors/apache-ah01909-certificate-does-not-include-id/
Last updated: 2026-11-10

**AH01909 的意思是：Apache 的 mod_ssl 发现某个虚拟主机的证书里，没有这个虚拟主机的 ServerName。** 它在加载配置时以警告级别记录，比如 `AH01909: www.example.com:443:0 server certificate does NOT include an ID which matches the server name`。Apache 照常启动，但用 ServerName 访问的客户端会看到域名不匹配的错误。最常见的原因是虚拟主机没写 ServerName、ServerName 写错了，或者证书文件指错了。

## 先读懂这条日志

“server certificate”前面那一段由虚拟主机信息拼成：`ServerName:端口`，再加 `:序号` 表示这是该虚拟主机里的第几张证书。以 `www.example.com:443:0` 为例：

| 部分 | 含义 |
|---|---|
| `www.example.com` | Apache 用来比较的 ServerName；没写 ServerName 时是它猜出来的名字 |
| `443` | 虚拟主机的端口 |
| `0` | 该虚拟主机里的第一张证书（`1` 表示第二张，比如 ECDSA 证书） |

mod_ssl 拿这个名字去和证书 SAN 里的 DNS 名称、以及证书的 CN 比较，通配符只匹配最左边一级，ServerAlias 里的名字不参与比较。

## 为什么 Apache 只是警告

Apache 没法知道客户端实际会用哪个名字访问：一个虚拟主机可能主要服务别名，可能在负载均衡后面按 IP 访问，也可能只是兜底，证书覆盖的是那些名字也说得通。所以 mod_ssl 只记一条警告继续运行。浏览器就没有这种宽容了：按 [RFC 9525](https://www.rfc-editor.org/rfc/rfc9525) 和各浏览器自己的规则，客户端访问的名字不在证书 SAN 里就会拒绝连接。AH01909 是浏览器报 [NET::ERR_CERT_COMMON_NAME_INVALID](https://ssl.sh/zh/errors/net-err-cert-common-name-invalid/)、[SSL_ERROR_BAD_CERT_DOMAIN](https://ssl.sh/zh/errors/ssl-error-bad-cert-domain/) 之前，服务器一侧的预警。

## 同类 mod_ssl 启动日志

mod_ssl 加载证书时会记录几条同类日志，其中只有 AH02572 会让 Apache 无法启动。

| 代码 | 级别 | 日志内容（节选） | 含义 |
|---|---|---|---|
| AH01906 | 警告 | server certificate is a CA certificate | 把 CA 证书配成了服务器证书 |
| AH01907 | 警告 | server certificate is not a leaf certificate | 证书允许继续签发下级证书 |
| **AH01909** | 警告 | server certificate does NOT include an ID which matches the server name | ServerName 不在证书里 |
| AH02572 | 紧急 | Failed to configure at least one certificate and key | 没有可用的证书和私钥，Apache 无法启动，见 [AH02572](https://ssl.sh/zh/errors/apache-ah02572-failed-to-configure-certificate/) |

## 常见原因（按出现频率排序）

AH01909 有 4 种常见原因，最常见的是 SSL 虚拟主机没写 ServerName。

### 1. SSL 虚拟主机没写 ServerName

发行版自带的默认配置（Debian/Ubuntu 的 `default-ssl.conf`，CentOS、Rocky 等系统 mod_ssl 自带的 `ssl.conf`）里没有 ServerName。Apache 文档写明：基于名称的虚拟主机没有 ServerName 时，会使用由系统主机名推导出的完整域名。所以日志里出现的往往是 `localhost`、机器名或者反向解析出来的名字。如果同时看到 AH00558（“Could not reliably determine the server's fully qualified domain name”），说明全局 ServerName 也没设置。

### 2. 证书没覆盖 ServerName

虚拟主机写的是 `ServerName example.com`，证书却只签了 `www.example.com`；或者拿 `*.example.com` 通配符证书去配主域名。看证书里有哪些名字：

```bash
openssl x509 -in /etc/ssl/certs/example.com.crt -noout -subject -ext subjectAltName
```

### 3. 虚拟主机指向了错误的证书文件

续期或迁移之后，`SSLCertificateFile` 还指向旧证书、系统自带的占位证书（Debian/Ubuntu 上的 `ssl-cert-snakeoil.pem`），或者另一个网站的证书。看每个虚拟主机加载的是哪个文件：

```bash
apachectl -S
grep -RniE 'ServerName|SSLCertificateFile' /etc/apache2/sites-enabled/ /etc/httpd/conf.d/ 2>/dev/null
```

`apachectl -S` 会列出每个虚拟主机所在的文件和行号，443 端口上有多个虚拟主机时能快速定位。宝塔面板用 Apache 时，站点配置在面板的网站目录下，改动前先在面板里查看对应站点的 SSL 设置。

### 4. 故意设置的兜底虚拟主机

专门用来回应未知域名的默认虚拟主机，无论配哪张证书都会报 AH01909。知道它的存在就无所谓。

## 一台 Apache 跑多个 HTTPS 站点时要注意什么

同一个 IP 和 443 端口上配了多个虚拟主机时，Apache 按客户端在 SNI 里发来的域名，去匹配各虚拟主机的 ServerName 和 ServerAlias。Apache 文档写明：如果一个都匹配不上，就用这组虚拟主机里**排在第一个**的那个。由此带来两个后果：

- 某个站点的 ServerName 写错了，用户访问它时会落到第一个虚拟主机上，拿到的是别人的证书，浏览器报域名不匹配。
- 排在第一个的虚拟主机往往是系统默认站点，它的 AH01909 警告最常见，也最容易被忽略。
- Apache 文档说明，用通配符包含的配置文件按字母顺序加载，所以 `000-default`、`default-ssl` 这类文件名会排在前面。想让某个站点做兜底，就让它的文件名排在最前；不想要兜底，就停用默认站点。

所以看到 AH01909 时，不要只盯着报警的那一个虚拟主机，最好用 `apachectl -S` 把 443 上所有虚拟主机的顺序和名字都过一遍，确认每个域名都能匹配到正确的虚拟主机和证书。

## 怎么修

每个 SSL 虚拟主机都要写明 ServerName，证书要覆盖它和所有 ServerAlias。

**1. 每个 SSL 虚拟主机都写明 ServerName**，其他名字写进 ServerAlias：

```apache
<VirtualHost *:443>
    ServerName example.com
    ServerAlias www.example.com
    SSLEngine on
    SSLCertificateFile    /etc/letsencrypt/live/example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
</VirtualHost>
```

从 Apache 2.4.8 起，`SSLCertificateFile` 可以同时包含中间证书，直接指向完整证书链文件即可。

**2. 申请覆盖所有名字的证书**，ServerName 和 ServerAlias 里的都要有：

```bash
sudo certbot --apache -d example.com -d www.example.com
```

详见 [Apache 配置 Let's Encrypt](https://ssl.sh/zh/guides/apache-lets-encrypt-certbot/)。宝塔用户可以在站点的“SSL”页面勾选所有域名重新申请，见 [宝塔面板配置 SSL](https://ssl.sh/zh/guides/bt-panel-ssl/)。

**3. 设置全局 ServerName**（`apache2.conf` 或 `httpd.conf`），让默认虚拟主机不再依赖系统主机名。

**4. 停用用不到的默认 SSL 站点**（Debian/Ubuntu 上执行 `a2dissite default-ssl`），它们通常只挂着占位证书。

## 怎么确认已经修好

修好的标志是错误日志里不再出现 AH01909，并且每个域名带 SNI 测试都拿到覆盖它的证书。

1. `apachectl configtest` 返回 `Syntax OK`，然后 `systemctl reload apache2`（或 `httpd`）。
2. 重载之后错误日志里不再出现 AH01909：

   ```bash
   grep AH01909 /var/log/apache2/error.log | tail -n 3
   ```

   CentOS、Rocky 等系统的日志在 `/var/log/httpd/` 下。
3. 带 SNI 逐个测试每个名字：`openssl s_client -connect example.com:443 -servername www.example.com </dev/null 2>/dev/null | openssl x509 -noout -ext subjectAltName`，再用 [SSL 检测](https://ssl.sh/zh/tools/ssl-checker/) 检查每个域名。
