当有人直接用IP地址或者一个不属于你的域名访问你的服务器时,Nginx默认会把请求交给第一个匹配的server块处理,或者落入默认的默认站点。更麻烦的是,在HTTPS场景下,TLS握手阶段服务器就会把证书发出去,扫描工具通过证书里的域名信息就能确认这台服务器上跑了哪些站点。为了解决这个问题,Nginx 1.19.4引入了一个非常干脆的指令——ssl_reject_handshake,它可以在握手还没完成时直接掐断连接。

ssl_reject_handshake 是什么,解决了什么问题
在介绍这个指令之前,先回顾一下传统的处理方式。早期很多运维同学的写法是配置一个默认server块,监听443端口,然后配合return 444。444是Nginx特有的状态码,含义是不返回任何响应直接关闭连接。这种方案在HTTP层是有效的,但问题在于它发生在TLS握手完成之后——也就是说,证书已经被发送给客户端了,SNI信息也已经暴露,隐藏站点信息的目的并没有真正达成。
而ssl_reject_handshake的思路完全不同。它工作在TLS握手阶段,当客户端发起ClientHello之后,如果这个连接匹配到了配置了该指令的server块,Nginx会直接返回一个握手失败的告警信息,然后中断连接。整个过程证书不会发送,会话密钥也不会协商,客户端得到的信息基本为零。对于使用工具批量扫描IP段、枚举SNI来找隐藏服务的探测行为,这个指令能起到釜底抽薪的作用。
这个指令于2020年11月发布的Nginx 1.19.4中首次加入,随后也向后合入到了1.18.0的稳定分支更新中。使用前建议通过nginx -v确认版本,太老的版本写进去会直接报unknown directive错误导致配置加载失败。
完整配置示例
下面是一个典型的配置结构。核心思路是:写一个catch-all的默认server块,它不配置任何证书,只配置拒绝握手;真正的业务站点放在独立的server块中,通过server_name精确匹配。
# 默认server块:拒绝所有未匹配的SSL握手
server {
listen 443 ssl default_server;
ssl_reject_handshake on;
}
# 真正的业务站点
server {
listen 443 ssl;
server_name www.ipipp.com;
ssl_certificate /etc/nginx/ssl/ipipp.com.pem;
ssl_certificate_key /etc/nginx/ssl/ipipp.com.key;
location / {
root /var/www/ipipp;
index index.html;
}
}
几个细节需要注意。第一,默认server块上虽然写了listen 443 ssl,但因为没有配置证书,Nginx允许这种写法的前提就是该块内启用了ssl_reject_handshake,二者搭配才不会在启动时报缺少证书的错误。第二,default_server参数确保所有SNI无法匹配的连接都落到这个块上,这个参数不能省。第三,如果你同时监听了IPv6,需要再加一行listen [::]:443 ssl default_server;,否则IPv6的探测请求依然会走到别的server块。
对于HTTP的80端口,如果也想做类似处理,可以保留传统方案,因为没有TLS握手这个环节,444依然是最佳选择:
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
return 444;
}
与return 444方案的对比及验证方法
把两种方案放在一起对比会更清晰。return 444发生在HTTP请求处理阶段,此时TLS握手早已完成,证书已交付,只是不返回HTTP响应就断开;ssl_reject_handshake发生在TLS握手阶段,证书根本不出门。对于普通浏览器用户,两者表现都是连接被重置,看起来差不多;但对于OpenSSL、nmap这类工具,前者能完整拿到证书链,后者只能收到一个handshake failure告警,信息量差别巨大。
配置完成后,可以用命令行工具验证效果。用不存在的域名或者直接用IP去连:
# 指定一个不属于该服务器的SNI去测试 openssl s_client -connect 服务器IP:443 -servername fake.ipipp.com # 期望输出中包含类似信息: # SSL handshake has read 0 bytes and written ... bytes # tlsv1 alert handshake failure
如果输出里出现了证书内容,说明请求没有落到拒绝握手的server块上,通常是default_server没加上,或者业务server块误占了默认位置(Nginx在找不到显式default_server时,会拿配置文件中字母序最前的server块当默认)。改完配置记得nginx -t检查语法,再nginx -s reload平滑加载。
最后提醒一点:如果你的服务器前面还有CDN或者负载均衡,客户端握手实际发生在CDN节点上,这个指令只能在源站直连场景下发挥作用。另外某些老旧监控探活工具使用的是不发送SNI的裸连接,它们同样会被拒绝,需要把探活目标改成正确的域名,避免误杀。整体来看,这是目前隐藏服务器证书信息、对抗SNI扫描最简洁有效的Nginx原生方案,配置成本几乎为零,值得在生产环境统一启用。
Nginxssl_reject_handshakeSSL握手修改时间:2026-09-08 05:04:24