必须新建专用default_server拦截块,仅含listen 443 ssl default_server和ssl_reject_handshake on,严禁证书指令;业务server须显式列出精确域名,禁用通配符和server_name _;配合return 444与80端口拦截实现tls/http双层防护。

用默认 Server 块配合 ssl_reject_handshake on 彻底隐藏网关的真实 HTTPS 证书信息,核心在于:让所有未匹配合法域名的 TLS 握手请求,在协议最底层就被静默拒绝——不发证书、不响应 ServerHello、不记录日志、不留任何指纹。
必须新建专用的 default_server 拦截块
不能复用已有网站配置,也不能在带证书的 server 块里加这行指令。正确做法是单独建一个仅用于兜底的 HTTPS server:
-
监听声明必须含
default_server:例如listen 443 ssl default_server;和listen [::]:443 ssl default_server;(IPv6 不可漏) -
块内严禁出现任何证书指令:删掉或注释掉
ssl_certificate、ssl_certificate_key、ssl_trusted_certificate等,否则 Nginx 启动失败或退化为发证行为 -
不要写
server_name _或通配符:留空或写server_name ""即可;加_反而可能被某些版本误匹配,削弱拦截效果
每个业务 server 必须显式限定合法域名
真实站点的 server 块要严格列出生产使用的精确域名,这是 ssl_reject_handshake 起效的前提:
- ✅ 正确示例:
server_name example.com api.example.com admin.example.com; - ❌ 禁止通配符:
server_name *.example.com;—— 它会让所有子域通过握手,等于放弃防护 - ❌ 禁止空
server_name或只写 IP:这会导致该块本身成为事实上的默认响应者,暴露证书
验证是否真正“零证书泄露”
不能只看浏览器报错,要确认服务器确实没发出任何证书数据:
- 执行:
openssl s_client -connect YOUR_IP:443 -servername fake.test.com 2>&1 | grep "subject="—— 输出应为空 - 执行:
curl -vI https://YOUR_IP 2>&1 | grep "SSL certificate"—— 不应出现证书相关字段 - 访问
https://YOUR_IP时,Chrome 显示ERR_SSL_UNRECOGNIZED_NAME_ALERT,Firefox 显示SSL_ERROR_UNRECOGNIZED_NAME_ALERT
搭配 HTTP 层 return 444 形成双保险
虽然 ssl_reject_handshake 在 TLS 层就断连,但仍有极少数客户端(如某些 Java HTTPS 实现)可能绕过 SNI 检查。此时可加一层防御:
- 在同一 default_server 块中追加:
return 444; - 对 80 端口也做同样处理:
server { listen 80 default_server; return 444; },防止 HTTP 层暴露服务指纹 - 注意:
return 444是 HTTP 层动作,它不会影响 TLS 握手阶段的拒绝逻辑,两者互不干扰,可共存











