手里有一台服务器,却要跑好几个网站——公司官网、商城、博客、还有给客户托管的小站,怎么让它们都跑在同一个 IP 上、互不干扰?这就是"多域名共用一台服务器"的经典需求。它涉及三个层面的知识:虚拟主机(让一个 IP 上跑多个站点)、DNS 解析(把不同域名指到同一台机器),以及 HTTPS 时代的 SNI(让多个域名共用一个 IP 也能各自用证书)。
这篇文章从原理讲到落地:先解释"基于名称的虚拟主机"如何在同一个端口上区分不同域名,再给出 Nginx 和 Apache 两套真实的虚拟主机配置,重点讲清 HTTPS 下多域名共用一个 IP 的关键——SNI 机制,以及它在老客户端上的兼容性问题。然后对比四种证书方案(单域名、多域名 SAN、通配符、多份证书)各自适合什么场景,最后讲目录/权限/日志的隔离和几个高频误区。读完你能在一台服务器上把多个域名稳稳地跑起来。
先建立核心认知:"一个 IP 跑多个域名"靠的是 HTTP 协议里的 Host 头,服务器根据 Host 决定返回哪个站点。HTTP/1.1 默认带 Host,所以同一 80 端口上放任意多个站点都没问题;HTTPS 因为要先握手加密,早期拿不到 Host,才需要引入 SNI 机制。
一、多域名共存的基本原理
同一个 IP、同一个 80 端口,服务器怎么知道该返回哪个网站?答案是基于名称的虚拟主机(name-based virtual host):
用户访问 www.a.com ─┐
用户访问 www.b.com ─┼─> 同一台服务器(同一个IP:80)
用户访问 www.c.com ─┘
服务器看请求里的 Host 头 → 匹配对应站点的配置 → 返回对应内容
- DNS 把
www.a.com、www.b.com、www.c.com都解析到同一台服务器的 IP。 - Web 服务器(Nginx/Apache)里为每个域名写一个
server(Nginx)或(Apache)块。 - 服务器收到请求后,读取 HTTP 请求头里的
Host字段,匹配到对应的站点配置,返回对应内容。
所以多域名共存的前提是浏览器发送 Host 头——HTTP/1.1 默认发送,无需额外操作。而 HTTPS 因为要"先加密再传 HTTP(含 Host)",握手时服务器还不知道你要访问哪个域名,这正是 SNI 要解决的问题(后面详述)。
二、DNS 与服务器准备
第一步是让域名都指向这台服务器。在域名解析控制台给每个域名添加 A 记录:
www.a.com A 203.0.113.10 # 服务器公网 IP
www.b.com A 203.0.113.10
www.c.com A 203.0.113.10
验证解析是否生效:
dig +short www.a.com
nslookup www.b.com
# 都应解析到同一台服务器 IP
也要注意服务器的防火墙(安全组)要放行 80/443。不同系统的防火墙命令不同:
# RHEL/CentOS/Rocky(firewalld)
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
# Debian/Ubuntu(ufw)
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
风险提示:修改防火墙规则可能影响所有入站连接。改动前先确认当前 SSH(22)端口已放行,避免把自己锁在外面;改动后用另一个终端验证连接再关闭原会话,保留 firewall-cmd --panic-off 或 ufw disable 作为应急回滚手段。
三、Nginx 虚拟主机配置
Nginx 用 server 块表示一个虚拟主机,server_name 指定该站点响应的域名。可以一个文件一个站点,也可以集中管理。
# /etc/nginx/conf.d/a.conf
server {
listen 80;
server_name www.a.com a.com;
root /var/www/a.com/html;
index index.html index.php;
access_log /var/log/nginx/a.access.log;
error_log /var/log/nginx/a.error.log;
}
# /etc/nginx/conf.d/b.conf
server {
listen 80;
server_name www.b.com b.com;
root /var/www/b.com/html;
index index.html;
access_log /var/log/nginx/b.access.log;
error_log /var/log/nginx/b.error.log;
}
要点:
server_name可以写多个,空格分隔;www.a.com和a.com都指向同一站点即可。- 匹配优先级:精确匹配 > 以
*开头的通配 > 以*结尾的通配 > 正则(~)> 默认(第一个server或显式default_server)。 - 一定要配 默认站点,挡掉直接用 IP 访问或 Host 不匹配的请求:
# 显式指定默认站点,返回 444(直接断开)或跳转到主站
server {
listen 80 default_server;
server_name _;
return 444;
}
- 改完校验并重载(这是多站点最容易出事的一步,务必先
-t):
nginx -t && nginx -s reload
Debian/Ubuntu 习惯用 sites-available + sites-enabled 软链管理:
# 启用一个站点
sudo ln -s /etc/nginx/sites-available/a.com /etc/nginx/sites-enabled/a.com
sudo nginx -t && sudo systemctl reload nginx
四、Apache 虚拟主机配置对比
如果用的是 Apache,写法不同但思路一致。RHEL 系主配置在 /etc/httpd/conf/httpd.conf,Debian 系在 /etc/apache2/sites-available/。
# RHEL/CentOS/Rocky:/etc/httpd/conf.d/a.conf
ServerName www.a.com
ServerAlias a.com
DocumentRoot /var/www/a.com/html
ErrorLog /var/log/httpd/a_error.log
CustomLog /var/log/httpd/a_access.log combined
# Debian/Ubuntu:/etc/apache2/sites-available/b.conf
ServerName www.b.com
ServerAlias b.com
DocumentRoot /var/www/b.com/html
ErrorLog ${APACHE_LOG_DIR}/b_error.log
CustomLog ${APACHE_LOG_DIR}/b_access.log combined
# Debian/Ubuntu 启用站点
sudo a2ensite b.conf && sudo systemctl reload apache2
# RHEL 系直接 reload 即可(conf.d 下的 .conf 自动加载)
sudo systemctl reload httpd
Nginx 与 Apache 虚拟主机的对照:
| 维度 | Nginx | Apache |
|---|---|---|
| 配置单元 | server {} |
|
| 域名指令 | server_name |
ServerName / ServerAlias |
| 站点根 | root |
DocumentRoot |
| 默认站点标记 | default_server |
第一个 |
| 配置目录 | /etc/nginx/conf.d/ |
/etc/httpd/conf.d/ 或 /etc/apache2/sites-* |
| 校验命令 | nginx -t |
apachectl configtest / httpd -t |
两套都能很好地支持多域名,选择更多取决于现有技术栈和团队熟悉度。
五、HTTPS 与 SNI:多域名共用一个 IP 的关键
到了 HTTPS,多域名共用一台服务器会遇到一个根本性难题。
问题:TLS 握手发生在 HTTP 之前,握手时服务器还不知道客户端要访问哪个域名(因为加密还没建立、HTTP 请求还没发来)。但在旧时代,一个 IP 上只能配一张证书用于握手——于是同一个 IP 上的多个 HTTPS 站点,客户端只能拿到第一张证书,访问其他域名就会域名不匹配报错。
SNI(Server Name Indication) 就是为此而生:客户端在 TLS 握手的 ClientHello 里提前带上要访问的域名,服务器据此选择对应的证书。
ClientHello (含 SNI: www.a.com) ──> 服务器
服务器按 www.a.com 选证书
ServerHello + www.a.com 的证书 ──> 客户端
所以HTTPS 下多域名共用一个 IP,依赖 SNI。现代浏览器、curl、主流手机都支持 SNI;只有极老的客户端(老版本 IE on XP、很老的 Android、部分老 SDK)不支持,它们会拿到默认站点的证书从而报错。
Nginx 中每个 server 各配各的证书即可:
server {
listen 443 ssl;
server_name www.a.com;
ssl_certificate /etc/nginx/ssl/a.com/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/a.com/privkey.pem;
}
server {
listen 443 ssl;
server_name www.b.com;
ssl_certificate /etc/nginx/ssl/b.com/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/b.com/privkey.pem;
}
注意:每个绑定了 ssl_certificate 的 HTTPS server 块,都应确保有对应的默认兜底(listen 443 ssl default_server),否则不支持 SNI 的客户端会落到某个随机站点。同一 IP 上多个 SNI 站点的证书由 Nginx 根据 server_name 自动选择,无需手动指定。
六、证书方案对比
多域名下的 HTTPS 证书,有四种常见方案,选择取决于域名数量和结构。
| 方案 | 覆盖范围 | 适用场景 | 特点 |
|---|---|---|---|
| 单域名证书 | 一个具体域名 | 站点少、域名互不相关 | 便宜,但每域名一张,管理繁琐 |
| 多域名 SAN 证书 | 一张含多个域名(SAN 列表) | 域名数量固定、有限(如 5~10 个) | 一张证书管多个域名,续期集中 |
| 通配符证书 | *.example.com 覆盖同域一级子域 |
同一主域下子域很多 | 只需一张,但只覆盖一级子域 |
| 多份独立证书 + SNI | 各自独立 | 域名分属不同主体、需独立管理 | 灵活,但每张都要续期 |
关键提醒:
- 通配符只覆盖一级:
*.example.com覆盖www.example.com、api.example.com,不覆盖a.b.example.com。且通常不覆盖裸域example.com(需额外配置)。 - SAN 证书有域名数上限:不同 CA 不同,常见几十到上百个,超了要换方案。
- 续期:无论哪种,都要设自动续期(如 certbot 的定时任务)和有效期监控,避免过期导致全站握手失败。
七、目录、权限与日志隔离
多站点共用一个服务器,隔离做不好会互相影响。建议:
# 每个站点独立目录
/var/www/a.com/html
/var/www/b.com/html
# 属主与权限:Web 用户可读
sudo chown -R nginx:nginx /var/www/a.com
# RHEL 系 Web 用户是 nginx,Debian/Ubuntu 是 www-data
sudo chmod -R 755 /var/www/a.com
# 日志按站点拆分(前面配置里已体现),便于分别分析和轮转
ls /var/log/nginx/*.access.log
# SELinux(RHEL 系)下新目录要打上正确的上下文标签,否则 403
sudo semanage fcontext -a -t httpd_sys_content_t "/var/www/a.com(/.*)?"
sudo restorecon -Rv /var/www/a.com
风险提示:批量修改目录属主/权限可能影响其他服务或导致权限过松。用 chmod -R 前先确认目录用途,避免把敏感文件设成全局可读;SELinux 变更后用 restorecon 校正,不要用关闭 SELinux 的方式绕过。
八、常见误区
- 没配默认站点:直接访问 IP 或未知 Host 时,落到第一个
server块,可能暴露不该暴露的站点。务必加default_server兜底。 - 以为 HTTPS 多域名只靠 DNS 就够:HTTPS 还需要 SNI 和每个域名的证书,DNS 只是第一步。
- DNS 用了泛解析
*.example.com但服务器没配对应站点:会落到默认站点,可能返回错误内容。 - 一个
server块里混放多个不相关域名的 root:正确做法是一个域名一个server块,server_name各写各的。 - 忽略证书匹配:SNI 站点要注意,不支持 SNI 的客户端会拿到默认证书,需评估是否有这类客户端。
常见问题
Q:同一个 IP 上能放多少个 HTTPS 站点?
从 SNI 机制上不设数量上限,实际受服务器内存、CPU、证书数量管理和运维复杂度影响。几十个站点共用一个 IP 很常见(经验值);再多建议按业务分组、用多个服务器或多个 IP 分摊。
Q:为什么 HTTPS 访问某个域名拿到了别的域名的证书?
通常是不支持 SNI 的客户端(老设备)访问时,服务器只能用默认站点的证书。也可能是该域名的 server 块缺少 ssl_certificate 配置。检查是否有 listen 443 ssl default_server 兜底,以及目标域名的证书配置是否正确。
Q:server_name 写 www.a.com 和 *.a.com 有什么区别?
前者只精确匹配 www.a.com;后者匹配 a.com 下任意一级子域(www.a.com、api.a.com…)但不含裸域 a.com。要同时覆盖,可写成 server_name a.com *.a.com;。
Q:HTTP 能正常访问,HTTPS 报证书错误,怎么排查?
先用 openssl s_client -connect IP:443 -servername www.a.com 看是否返回了正确证书。若证书不对,检查该域名的 server 块证书路径、是否被默认站点抢先匹配。HTTP 正常说明 DNS 和虚拟主机配置基本没问题,问题在 TLS 层。
Q:多域名各自独立证书,续期很麻烦怎么办?
可以用脚本批量续期并统一 reload,或用 LE 的 DNS 验证方式给通配符证书续期(一张覆盖所有子域)。关键是把"续期 + reload"自动化,并加有效期告警,避免漏续导致握手失败。
总结
多个域名共用一台服务器的核心是基于名称的虚拟主机:DNS 把各域名指向同一 IP,Web 服务器按请求的 Host 头匹配对应 server/VirtualHost 块。
Nginx 用 server 块加 server_name,Apache 用 加 ServerName;务必配置默认站点兜底,避免未知 Host 落到错误站点。
HTTPS 下多域名共用一个 IP 依赖 SNI 机制(握手时携带域名以选择证书);现代客户端都支持,但老客户端可能拿到默认证书报错,需评估兼容性。
证书方案按域名数量选择:单域名证书、多域名 SAN 证书、通配符证书(只覆盖一级子域)或各自独立证书加 SNI,并务必做自动续期与有效期告警。
隔离要做好:每站点独立目录与权限、日志按站点拆分、SELinux 目录打对标签;改配置统一走 nginx -t 校验、reload 生效、备份保底。
企业QQ咨询




