ssl_reject_handshake on 必须配置在仅监听443端口且带default_server标识的兜底server块中,该块不可含ssl_certificate等证书指令,否则启动报错或不生效;它与return 444配合可实现TLS层和HTTP层双重拦截。

必须用 ssl_reject_handshake on 配在 default_server 的 443 server 块里,且该块不能配任何证书;否则要么不生效,要么启动报错。
为什么 ssl_reject_handshake 不生效
常见错误是把它塞进已有网站的 server 块里,或者和 ssl_certificate 放在一起。这个指令只在「没有匹配到有效 server_name」时起作用,前提是它自己所在的块被识别为兜底入口:
-
listen 443 ssl default_server缺一不可——default_server才能捕获 IP 直连或 SNI 为空/无效的请求 - 该块中**绝对不能出现**
ssl_certificate或ssl_certificate_key,否则 Nginx 启动直接失败(报错:*no "ssl_certificate" is defined*) - 如果用了 IPv6,记得加
listen [::]:443 ssl default_server,否则 IPv6 地址直连仍会泄露证书 - 该指令属于
ngx_http_ssl_module,不是 stream 模块——配置位置必须在http块内,而非stream块
ssl_reject_handshake 和 return 444 能不能一起用
可以,但角色不同:前者在 TLS 握手阶段就断开连接(不发证书、不发 ServerHello),后者在 HTTP 层关闭连接(已建立 TLS,但不返回任何响应体)。实际部署中建议两者共存,形成双保险:
-
ssl_reject_handshake on是主防线,拦截 99% 的扫描器和 curl -k 测试 -
return 444是后备,防极少数绕过 TLS 层检测的客户端(比如某些旧版 Java HTTPS 客户端) - 注意:
return 444必须写在ssl_reject_handshake on同一server块里,否则没意义
验证是否真生效:别只看浏览器报错
浏览器访问 https://你的IP 显示 ERR_SSL_UNRECOGNIZED_NAME_ALERT 只说明 TLS 层被拒,但得确认没发证书。更可靠的验证方式是:
- 用
openssl s_client -connect 你的IP:443 -servername example.com 2>&1 | grep "subject="—— 如果输出空,说明没发证书;如果有输出,说明配置没生效 - 用
curl -vI https://你的IP 2>&1 | grep "SSL certificate"—— 正常应无输出;若有,说明证书还是被送出去了 - 检查 Nginx 错误日志:
tail -f /var/log/nginx/error.log,启动时若报ssl_reject_handshake相关警告,说明模块未启用或版本太低
最容易被忽略的是:这个机制只管 TLS 握手,不管 HTTP Host 头。如果攻击者用 curl -k -H "Host: evil.com" https://你的IP,只要 SNI 匹配了某个合法站点,ssl_reject_handshake 就不会触发——这时候得靠后端应用或 WAF 做 Host 头校验。











