关键在于配置listen 443 ssl default_server兜底块并返回403或444,配合server_name _和有效证书(或ssl_reject_handshake on),确保未匹配域名的HTTPS请求被主动拦截,而非落入业务块暴露资源。

要防止非法域名通过 IP 直接访问或恶意解析到你的服务器并“窃取”SSL 流量(即 HTTPS 443 端口被未授权域名复用),关键不是靠证书本身,而是靠 Nginx 在 TLS 握手后、解密前就完成的 server_name 匹配——这依赖 SNI(Server Name Indication)机制。Nginx 会根据客户端 ClientHello 中声明的域名,精准选择对应 server 块和证书。但若没有严格兜底,默认行为仍可能暴露资源。
明确只响应已知域名,拒绝一切未知请求
核心原则:每个 listen 443 ssl 的 server 块必须显式声明 server_name,且不能留空或仅用 IP。同时,必须配置一个专门的“兜底 server”,排在 conf.d 所有文件最前面(按字典序或加载顺序),对所有未匹配域名返回 403 或重定向到空白页。
- 在
/etc/nginx/conf.d/00-default-ssl.conf(确保文件名排序靠前)中写入:
server {
listen 443 ssl http2 default_server;
server_name _;
ssl_certificate /path/to/empty.crt; # 可用自签证书,或复用任意有效证书
ssl_certificate_key /path/to/empty.key;
return 403;
}
注意:default_server 标志和 server_name _ 组合,确保它成为所有未命中其他 server_name 的最终 fallback。即使没配证书,Nginx 也能建立 TLS 连接并返回 403;若完全不想加载证书,也可改用 listen 443 ssl http2 default_server; + ssl_reject_handshake on;(Nginx 1.19.0+ 支持),直接拒绝握手。
每个业务域名单独配置,禁用泛匹配
为每个合法域名(如 app.example.com、api.example.com)创建独立配置文件,不使用通配符(如 *.example.com),除非你明确需要且已评估安全风险。
- 每个 server 块写死具体域名:
server_name app.example.com; - 避免
server_name example.com *.example.com;这类混用,否则子域攻击面扩大 - 多个域名用空格分隔:
server_name www.example.com example.com;
利用 $host 变量做二次校验(增强层)
在业务 server 块内加一层运行时判断,进一步拦截伪造 Host 头的请求(如 curl -H "Host: evil.com" https://your-ip):
if ($host != "app.example.com") {
return 444; # 444 是 Nginx 特有“关闭连接”状态码,比 403 更静默
}
⚠️ 注意:该 if 语句需放在 location 外层,且仅用于兜底校验;主要匹配逻辑仍应由 server_name 完成,因为 if 在 SSL 解密后执行,无法阻止证书误配。
验证配置是否生效
测试三类典型场景:
- 用正确域名访问:
curl -I https://app.example.com→ 应返回 200 - 用 IP 直接访问:
curl -k -I https://1.2.3.4→ 应返回 403 或无响应(444) - 用非法域名访问:
curl -k -H "Host: bad.com" https://1.2.3.4→ 同样应被拦截
检查 Nginx 错误日志:tail -f /var/log/nginx/error.log,确认未匹配请求是否进入兜底块。


















