ssl_reject_handshake on 是 Nginx 1.19.4+ 真实存在的官方指令,必须置于 http 块顶层,配合精确 server_name 列表和禁用 default_server,可在 TLS 握手阶段直接拒绝未匹配 SNI 的请求,不发证书、不留痕迹。

OpenResty 或 Nginx 并没有内置名为 ssl_reject_handshake 的配置变量。这个名称是常见误解,可能源于对 TLS 握手控制机制的模糊理解。实际中,**无法在 SSL/TLS 握手完成前(即 SNI 解析阶段)直接通过一个开关变量“拒绝握手”**,但可以通过合理组合 Nginx/OpenResty 的指令,在 SNI 可用后、证书协商前,实现对非法域名的高效拦截。
核心原理:利用 SNI 和 server_name 匹配提前终止
Nginx 在 TLS 握手过程中,客户端会通过 Server Name Indication (SNI) 扩展明文发送请求的域名。Nginx 能在收到 SNI 后、验证证书前,根据 server_name 指令匹配虚拟主机。若未匹配到任何合法 server { ... } 块,且没有默认 server,Nginx 会返回 421 Misdirected Request(HTTP/2)或直接关闭连接(HTTP/1.x),效果等同于“拒绝握手”。这是最轻量、最标准的拦截方式。
- 确保每个 HTTPS server 块都明确声明
server_name example.com www.example.com; - 设置一个 默认的 catch-all server,监听相同端口但不指定有效域名,用于统一拦截非法请求:
server {
listen 443 ssl http2 default_server;
server_name _; # 匹配所有未被其他 server_name 显式覆盖的 SNI
<pre class="brush:php;toolbar:false;"># 不加载任何有效证书(或用自签名占位,但不推荐)
ssl_certificate /dev/null;
ssl_certificate_key /dev/null;
# 立即返回错误并关闭连接
return 444; # Nginx 非标准码,表示直接关闭 TCP 连接}
注意:return 444 是 Nginx 特有行为,能快速切断连接,避免后续处理开销;它发生在 TLS 握手完成前,用户侧通常看到的是 “SSL_ERROR_HANDSHAKE_FAILURE_ALERT” 或连接被重置。
进阶控制:用 lua-nginx-module 动态校验 SNI(OpenResty)
若需更灵活逻辑(如白名单校验、数据库查询、正则匹配),可在 ssl_certificate_by_lua_block 阶段操作 —— 这是 OpenResty 提供的、在证书选择前执行 Lua 的唯一时机,可安全访问 SNI:
- 该阶段运行在 SSL 上下文中,不能使用大多数 ngx.* API,但可调用
ngx.var.ssl_server_name - 若 SNI 不合法,调用
ngx.exit(444)可立即终止握手
server {
listen 443 ssl;
ssl_certificate /path/to/default.crt;
ssl_certificate_key /path/to/default.key;
<pre class="brush:php;toolbar:false;">ssl_certificate_by_lua_block {
local sni = ngx.var.ssl_server_name
if not sni or not string.match(sni, "^%w+%.example%.com$") then
ngx.exit(444)
end
}
# 后续 location 等配置...}
⚠️ 注意:此方法要求 OpenResty + lua-nginx-module,且 ssl_certificate_by_lua_block 中不能做阻塞 I/O(如同步 DB 查询)。如需查库,应改用 cosocket 异步方式或前置缓存校验。
不推荐的做法:依赖 ssl_preread 模块做四层拦截
stream { ssl_preread on; } 可在 TCP 层提取 SNI,但属于四层代理,无法与 HTTP server 块共享上下文,也不能触发证书加载或 return 444。它适合透传或简单路由,但不适合需要证书校验、HTTP 语义或精细策略的场景**。强行在此阶段拦截,会导致 TLS 握手失败,客户端错误信息不明确,运维排查困难。
验证与调试关键点
拦截是否生效,不能只看浏览器——需用命令行工具观察底层行为:
- 用
openssl s_client -connect your.site:443 -servername evil.com测试非法 SNI,应无证书返回且连接快速关闭 - 检查 Nginx error log,正常拦截不会报错;若出现
no "ssl_certificate" is defined,说明默认 server 缺少证书配置(可忽略,因目标就是拒绝) - 确认未启用
ssl_session_cache等可能导致缓存绕过的行为
不复杂但容易忽略。











