只有一个注册域名,也可以部署多个网站。主域名展示个人主页,blog.example.com 放博客,tools.example.com 放工具,它们可以共用一台服务器的公网 IP,也可以共用 80 和 443 端口。
真正需要设计的是请求到达服务器之后如何分流。当前怪默 Hexo 项目的服务器文件入口是 /var/www/maysea/current;本文中 example.com、203.0.113.10 和应用内部端口均为说明用占位值。域名与 HTTPS 配置是示例方案,不表示本文已替某个域名修改解析或证书。
沿着一次请求区分四个名字
访问 https://blog.example.com/posts/note/ 时,浏览器先获取目标地址,再连接 HTTPS 端口,握手阶段发送服务器名称信息,最后在加密通道里发送 HTTP 请求。排查时应分别问:解析到哪里、连哪个端口、拿到谁的证书、返回哪个网站。
普通 A 记录返回 IPv4 地址,CNAME 建立名称别名关系,不能在 A 记录里填 IP:3000 来选择网页端口。DNS 还有其他记录类型,但不能据此假设普通浏览器会按自定义服务记录访问网站。缓存和 TTL 意味着解析更新不会让所有客户端在同一时刻获得新答案。DNS 基础与缓存模型 RFC 1034
假设博客和工具都指向 203.0.113.10,它们的连接目标可以完全相同;浏览器仍然保留各自访问的主机名,Nginx 再按名称选择站点。端口没有被“分配给域名”,而是由服务器上的监听程序接收连接。
| 层次 | 本例需要确认的值 | 常见故障表现 |
|---|---|---|
| DNS | blog 与 tools 的 A、AAAA 或 CNAME | 访问旧服务器、部分网络失败 |
| TCP 入口 | 公网 80、443 能否建立连接 | 超时、连接被拒绝 |
| TLS | 实际访问名称对应的证书 | 名称不匹配、证书链错误 |
| HTTP 路由 | Host、监听地址、server_name | 能打开却显示另一网站 |
| 站点资源 | root 或上游地址、文件权限 | 404、403、502 |
Nginx 在同一个端口上如何分流
Nginx 先按连接地址和端口确定候选 server,再按请求名称选择配置。未匹配时使用该监听入口的默认服务器,默认属性属于监听地址和端口,而不是某个 server_name 字符串。Nginx 请求处理规则
静态博客可以直接读取构建结果,无需把 Hexo 开发服务器常驻线上:
1 | server { |
try_files 在这里表达一个有用边界:不存在的静态文章返回 404。不要为了让首页“总能打开”把所有错误路径都回退到首页,否则搜索引擎、监控与用户看到的是假成功,真正缺失的图片也更难定位。
动态应用使用独立的内部端口
工具服务可以只监听 127.0.0.1:3000,由另一个站点块转发。多个后端不能同时占用相同地址和端口;可以用不同回环端口,或采用独立 Unix socket。对访客仍统一暴露常规网页入口。
1 | server { |
这只是普通 HTTP 代理。WebSocket、长上传或流式响应要按协议补充配置。后端若根据转发头生成绝对链接或判断客户端地址,应只信任经过验证的代理链;客户端自己提交的头不能天然成为身份依据。
同机隔离还要考虑应用进程权限、数据目录与资源预算。Nginx 能按名称分流,却不会自动阻止工具应用读到博客源码,也不会替数据库设置内存限制。把不同网站放在不同端口,并不等于完成了安全隔离。
再考虑一个实际调试场景:浏览器通过 HTTPS 进入 Nginx,后端内部连接却是 HTTP。若应用不知道原始协议,可能生成 HTTP 登录回跳或错误的绝对资源地址;如果它和代理分别强制跳转,还可能形成循环。应确认应用支持可信的转发协议配置,而不是把所有重定向都关掉。规范主机名跳转也应使用明确目标,不能无条件拼接任意客户端提交的 Host。这样排查“打开首页正常,登录后失败”时,就知道继续检查应用对外部地址的理解,而不是再次修改域名解析。
先绕过 DNS,证明站点路由正确
在服务器本机用指定 Host 访问回环入口,可以测试 HTTP 虚拟主机,而不用等待 DNS。下列命令只读请求,不修改解析或服务配置。
1 | curl --fail --show-error --head \ |
如果工具站返回 502,先请求 http://127.0.0.1:3000/,确认上游是否存在;若博客返回 403,检查 Nginx worker 对路径上每级目录的遍历权限和首页文件权限;如果两个名称都返回博客,检查启用的配置与 server_name 是否正确,而不是反复改 DNS。
nginx -t 通过证明配置可解析并完成相关检查,不证明公网防火墙开放,也不证明页面内容正确。新增站点应保留独立配置和既有配置备份,确认通过再重新加载,避免为一个博客直接替换全部 nginx.conf。
HTTPS 错误发生在 HTTP 路由之前
HTTPS 握手时还没有普通 HTTP Host 请求头,因此只用 curl -H 'Host: ...' https://服务器IP/ 不是完整的域名测试。服务器需要依据 TLS 中的名称选择证书;证书还必须覆盖浏览器真正访问的名称。多个 HTTPS 网站可借助 SNI 共用 IP 与端口。Nginx HTTPS 与 SNI 说明
更合适的测试是临时指定域名到 IP 的映射,同时保留 URL 中的域名,使证书校验与站点路由使用真实名称:
1 | # 将文档专用示例 IP 换成已配置好证书的实际服务器 IP。 |
--resolve 只对这次 curl 调用提供解析映射,不会修改系统 DNS。不要加 -k 后再宣称 HTTPS 正常,因为这样跳过了证书验证。curl 的解析覆盖说明
根域名、www 和 blog 是不同名称,证书覆盖范围要逐个确认。泛域名证书也不应被理解成自动覆盖根域和任意深度子域。若实际配置只覆盖 blog.example.com,使用 IP 访问出现证书名称错误可能是预期行为,不能据此认定整个站点不可用。
“我的手机不行,电脑正常”怎样排查
先保留两端完整错误:是解析失败、连接超时、证书错误,还是出现另一网站。再查询 A 与 AAAA。一个常见假设是 A 已指向新主机,但 AAAA 仍指向旧 IPv6 入口,不同网络选择不同地址,于是表现不一致。应验证后再修正记录,而不是看到 AAAA 就立即删除。
Linux 上可以比较:
1 | dig +short A blog.example.com |
这些工具不是所有系统默认自带。Windows 可使用 Resolve-DnsName 查询,并用 curl.exe 避免命令别名差异。IPv6 测试失败也可能是本地网络没有 IPv6,需结合实际连通性判断。
另一种情况是递归解析器仍缓存旧记录。比较权威服务器与客户端使用的递归解析器结果,确认 TTL 和变更时间;不要把“DNS 传播”当成一切问题的解释。解析值完全正确但 443 连接不通时,应继续查安全组、主机防火墙、Nginx 监听和公网路由。
子域名与路径前缀怎样选择
给每个应用使用子域名,往往更容易维护独立站点配置、证书与应用根路径。把工具放到 /tools/ 也可以,但应用必须支持该基础路径,否则资源可能仍请求 /assets/,登录跳转也可能丢失前缀。仅改一条 proxy_pass 很难修复所有前端假设。
同一主域下的 Cookie 范围、跨域请求与登录策略也应明确。共享域名带来便利,同时意味着一个应用设置过宽 Cookie 范围时可能影响另一个应用。个人项目可以先选择清晰的子域名边界,等真正需要共享登录时再设计协议。
最后建立一个验收矩阵:每个名称都验证 HTTP、HTTPS、首页、静态资源、错误路径,以及外部网络访问。新增一个网站应只增加明确的入口和目录,不应以“主站还能打开”作为其他网站也正确的证明。