如何基于请求域名动态设置Apache SSL证书及解决虚拟主机冲突
原生mod_ssl模块的SSLCertificateFile系列指令不支持mod_vhost_alias提供的%1这类动态占位符。Apache启动阶段就会预读取证书路径做校验,此时还没有收到用户请求的域名信息,%1不会被替换为实际域名,因此会直接报路径不存在的错误。
修复方法
优先使用Apache 2.4.30及以上版本自带的mod_md模块实现动态证书加载,配置示例如下:
# 提前加载mod_md模块 MDomain example1.com example2.com example3.com MDCertificateAgreement accepted # 指定证书存储根目录 MDStoreDir /home/abc/certs <VirtualHost *:443> SSLEngine on # 无需单独指定每个域名的证书路径,mod_md会自动匹配 </VirtualHost>
如果需要自定义证书路径规则,也可以配合SNI功能,使用SSLCertificateFile "/home/abc/certs/%{SERVER_NAME}/domain.crt"格式的变量写法,注意需要确保Apache版本支持运行时替换SSL证书路径变量。
数据库读取SSL配置支持
原生Apache不支持直接从数据库读取SSL证书配置,需要额外安装第三方模块如mod_vhost_dbd实现,数据库中仅需存储域名和证书路径的映射关系即可,证书文件本身仍建议存储在本地文件系统保证读取性能和安全性。
全局配置的VirtualDocumentRoot优先级高于所有虚拟主机内的DocumentRoot规则,因此会覆盖你单独配置的dev.example.com站点。如果你把VirtualDocumentRoot放到单个虚拟主机内,其他未匹配到虚拟主机规则的域名会默认走加载顺序第一个的虚拟主机(也就是你配置的dev站点),所以会返回默认页面。
修复方法
删除全局的VirtualDocumentRoot配置,按静态虚拟主机在前、动态默认虚拟主机在后的顺序配置即可,示例如下:
# 静态站点:dev.example.com,放在最前面优先匹配 <VirtualHost *:80> ServerName dev.example.com DocumentRoot /var/www/html/devportal <Directory "/var/www/html/devportal"> Options Indexes FollowSymLinks Multiviews AllowOverride All Require all granted </Directory> </VirtualHost> # 动态虚拟主机:所有未匹配到的域名走该规则,放在最后 <VirtualHost *:80> ServerName dummy.default ServerAlias * UseCanonicalName Off VirtualDocumentRoot /var/www/html/%1 <Directory /var/www/html> Options Indexes FollowSymLinks MultiViews AllowOverride All Require all granted </Directory> </VirtualHost>
注意Apache 2.4版本已经废弃Order allow,deny、allow from all这类旧指令,直接保留Require all granted即可,避免规则冲突。
内容的提问来源于stack exchange,提问作者Abishek

