前几天顺手检查服务器,发现一个有点意外的事:
- 用域名访问,各个站都正常;
- 用
http://<服务器IP>/访问,打开的不是我那个”主力站”,而是我后来放上去的一个小工具。
第一反应是”谁把我的 IP 占了”,查完才明白:IP 从来没有属于过任何域名站点,它只是一直没有 server 块来认领它而已。
顺便把”域名 / IP / 端口”三者到底怎么分流的规则彻底理清楚了,记一篇备查。
一、先把这台服务器上的东西摊开看
面板(宝塔 + Nginx)里和配置文件里实际是这样:
| 站点 | 标识(域名 / IP) | 监听端口 | 站点根目录 |
|---|---|---|---|
| 主力站 | dddwk.com、www.dddwk.com |
80 + 443 | /www/webroot/www.dddwk.com |
| 博客 | haoyelaiga.com、www.haoyelaiga.com |
80 + 443 | /www/webroot/haoyelaiga.com |
| 测试站(DevKit) | 121.2*0.77.144(就是 IP 本身) |
99 | /www/webroot/docusaTest |
| 生词复习工具 | 121.2*0.77.144(也是 IP) |
80 | /opt/word-review |
注意最后两行:两个不同的站,标识都是同一个 IP,只是端口不同。
为什么前面两个域名站能一起挤在 80 上,后面这个却必须另开端口?这就是这篇要说的。
二、Nginx 分流靠的是「请求里的域名」,不是端口
先说最容易误会的一点:端口不是”站点编号”。
listen 80; ← 这个端口上可以并列很多个 server 块
server_name a.com b.com; ← 一个块可以接很多个域名
一个 listen 上能挂无数个 server 块,Nginx 收到请求后按请求头里的 Host(也就是你在浏览器里输入的域名)去挑:
flowchart TB
R["浏览器请求<br/>Host: dddwk.com"] --> N["Nginx :80<br/>同一份监听"]
N --> S1["server 块 A<br/>server_name dddwk.com"]
N --> S2["server 块 B<br/>server_name haoyelaiga.com"]
N --> S3["server 块 C<br/>server_name 121.2*0.77.144"]
N --> S4["0.default.conf<br/>兜底块"]
S1 --> W1["主力站根目录"]
S2 --> W2["博客根目录"]
S3 --> W3["反代 127.0.0.1:8765"]
S4 --> W4["默认页"]
dddwk.com 和 www.dddwk.com 访问到的都是同一个 server 块 —— 所以域名要多少有多少,全都可以用 80 承接。这也是主机商敢在一台机器上卖”无限网站”的原因。
HTTPS 同理,只是区分依据从 Host 换成了 TLS 握手里的 SNI(域名),所以 443 也能被无限个站点共用。
写成配置就一句话的事,域名并排放在 server_name 里:
server {
listen 80;
listen 443 ssl http2;
server_name dddwk.com www.dddwk.com; # 多少个都行
root /www/webroot/www.dddwk.com/;
}
三、到底选哪个 server 块:匹配是有优先级的
不是”写在前面就赢”,Nginx 有明确顺序:
| 优先级 | 规则 | 例子 |
|---|---|---|
| 1 | 精确匹配 server_name |
server_name dddwk.com; |
| 2 | 以 * 开头的通配 |
server_name *.example.com; |
| 3 | 以 * 结尾的通配 |
server_name example.*; |
| 4 | 正则(按配置出现顺序) | ~^www\d+\.example\.com$ |
| 5 | 都不匹配 → 该端口的默认站 | 见下一节 |
所以只要有个块写了 server_name 121.2*0.77.144;,用 IP 访问时它就是精确命中,优先级高于任何兜底 —— 这正是开头那个”意外”的真实原因。
四、谁都没认领时,请求会落到哪
那个”默认站”,如果没有显式写 default_server,取的是配置里第一个监听该端口的 server 块(宝塔里就是按文件名排序后那个 0.default.conf)。
副作用挺反直觉:
- 用一个不存在的域名访问这台服务器,会落到默认站,返回一个 nginx 的 404 页面 —— 而 HTTP 状态码很可能还是 200,监控脚本、爬虫很容易被它骗过去(”站点正常?”其实什么也没匹配上)。
- 用 IP 访问也一样,直到有人用
server_name <IP>把这个 Host 认领掉。
五、为什么纯 IP 站点必须换端口
因为 IP 站没有域名可用来区分,它的身份就是 IP 本身。两个块的 Host 都是 121.2*0.77.144,而同一个端口上,同一时刻只有一个块能生效:
- Nginx 启动时会警告:
conflicting server name "121.2*0.77.144" on 0.0.0.0:80, ignored - 实际生效的只有先匹配上的那个,另一个永远收不到请求
| 站点标识 | 能不能共用 80 | 原因 |
|---|---|---|
| 域名(不管几个) | ✅ 可以无限共用 | 靠 Host / SNI 区分,彼此不冲突 |
| 纯 IP | ❌ 只能有一个 | 标识就是 IP,没有第二个区分维度 → 只能换端口 |
所以那台服务器上,工具站用了 80,DevKit 只能去 99。这不是”新站必须新端口”,而是”两个站的身份撞车了”。
别再给”IP 站”加端口了。想腾位置最干净的办法是给它一个域名 —— 有了域名,它能和别的站一起待在 80/443,证书、分享卡、SEO 全都顺了。
六、纯 IP 站点还有两个隐性代价
- 上不了正常 HTTPS:主流免费证书签发的都是域名证书,IP 证书是近年的新特性且有效期极短。纯 IP 站基本只能跑 HTTP,而 HTTP 属于非安全上下文 —— 浏览器的
navigator.clipboard、Service Worker 这类能力直接不可用(我在那个小工具里就踩到了:”复制段落”按钮必须退回老 API 兜底)。 - 非标准端口要额外放行:80 / 443 是云厂商安全组默认开的,99 这种得自己去控制台放行,还容易和别的服务撞端口。
七、实操:不改 DNS 也能验证分流
最实用的一条 —— curl 直接伪造 Host 头,就能看到某个域名会被分到哪个站:
# 连 IP,但假装自己在访问某个域名
curl -I -H 'Host: dddwk.com' http://<服务器IP>/
# 看默认站兜底行为(乱写一个不存在的域名)
curl -I -H 'Host: whatever-not-exists.invalid' http://<服务器IP>/
# 列出 80 端口上所有 server_name 和 listen
nginx -T 2>/dev/null | grep -E 'server_name|listen'
nginx -T 会把所有 include 进来的配置一次性打印出来,比一个个文件翻快得多。
结论
- 域名可以有无数个,全部指向同一台服务器、全部用 80 承接 —— 靠
server_name+Host头,HTTPS 靠 SNI。 - 纯 IP 不行:它的标识就是 IP,两个站撞同一个 Host,Nginx 只认一个,只能靠不同端口区分。
- 别把 IP 直连当成”主站入口”:它拿不到正常证书、不利于分享和 SEO;主站该由域名承载,IP 只适合放内部工具(再套个 Basic Auth)。
背景:那台服务器上工具站的部署记录在《把生词复习工具搬到腾讯轻量服务器》和《动手篇:按需唤醒与三个坑》两篇里。
本文章永久链接: 为什么多个域名能共用一个 80 端口,IP 站却必须换端口

