服务器和代理

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

作者: · 最后核实

报错信息AH01909: www.example.com:443:0 server certificate does NOT include an ID which matches the server name

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 和各浏览器自己的规则,客户端访问的名字不在证书 SAN 里就会拒绝连接。AH01909 是浏览器报 NET::ERR_CERT_COMMON_NAME_INVALID、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

常见原因(按出现频率排序)

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 通配符证书去配主域名。看证书里有哪些名字:

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

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

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

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:

<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 里的都要有:

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

详见 Apache 配置 Let's Encrypt。宝塔用户可以在站点的“SSL”页面勾选所有域名重新申请,见 宝塔面板配置 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 检测 检查每个域名。

常见问题

AH01909 可以不管吗?

只有在你清楚它为什么出现时可以,比如一个专门兜底、从不服务真实流量的默认虚拟主机。其他情况下,用 ServerName 访问的用户会看到 NET::ERR_CERT_COMMON_NAME_INVALID 这类域名不匹配错误。

证书是 *.example.com 通配符,为什么 example.com 还报 AH01909?

通配符只匹配最左边一级:*.example.com 能覆盖 www.example.com,覆盖不了 example.com,也覆盖不了 a.b.example.com。申请证书时把主域名单独加上。

AH01909 会检查 ServerAlias 吗?

不会,mod_ssl 只比较虚拟主机的 ServerName。ServerAlias 里的名字同样必须在证书里,但 Apache 不会为它们报警,要用 openssl s_client -servername 逐个测试。

虚拟主机标识最后的 :0 是什么意思?

是证书在该虚拟主机里的序号。同时配了 RSA 和 ECDSA 两张证书时,会看到 :0 和 :1,哪张证书缺域名就为哪张报一次。

资料来源

  1. Apache httpd 源码:ssl_engine_init.c(AH01909)
  2. Apache HTTP Server: ServerName 指令
  3. Apache HTTP Server: 基于名称的虚拟主机
  4. Apache HTTP Server: mod_ssl
  5. RFC 9525: TLS 中的服务身份