$ssl_server_name可稳定记录客户端TLS握手时的SNI域名,需Nginx≥1.7.0、启用SSL模块、配置有效证书且避免强验证干扰;推荐用log_format定义结构化日志并仅在HTTPS server块中启用。

直接使用 $ssl_server_name 可以在 Nginx 日志中稳定记录客户端在 TLS 握手阶段通过 SNI 扩展发送的目标域名,但前提是连接已成功完成 TLS 握手且请求进入 HTTP 处理流程。它不等同于抓取原始 ClientHello 报文,但对生产环境下的 SNI 域名分布统计、多租户识别和异常域名筛查非常实用。
确认 Nginx 版本与模块支持
$ssl_server_name 是 Nginx 1.7.0+ 引入的变量,依赖 ngx_http_ssl_module(默认启用)。需确保:
- Nginx ≥ 1.7.0(推荐 ≥ 1.19.0,修复早期版本在重协商或某些 TLS 1.3 场景下为空的问题)
- 配置了有效的 SSL 证书(否则 HTTPS 请求无法建立,该变量无意义)
- 未启用
ssl_verify_client optional_no_ca等干扰握手流程的强验证配置(可能影响变量赋值稳定性)
定义含 SNI 域名的日志格式
推荐使用竖线 | 分隔、字段固定、便于解析的结构化格式:
log_format sni_audit '$time_iso8601|$remote_addr|$ssl_server_name|$host|$request_method|$request_uri|$status|' '$ssl_protocol/$ssl_cipher|$request_time|$upstream_response_time|' '"$http_user_agent"|"$http_referer"|'$request_id';
-
$ssl_server_name:客户端在 ClientHello 中声明的 SNI 域名(如api.example.com),若未发送 SNI 或为 IP 直连,则为空字符串 -
$host:HTTP 请求头中的 Host 字段(可能被伪造或与 SNI 不一致),用于比对校验 -
$ssl_protocol和$ssl_cipher:辅助判断协议兼容性与加密强度,排查弱协议访问 - 所有时间字段统一用
$time_iso8601,避免时区混乱
在 server 块中启用并绑定日志
仅在启用 HTTPS 的 server 块中配置,避免 HTTP 块误用导致空值污染:
server {
listen 443 ssl http2;
server_name _;
ssl_certificate /path/to/default.crt;
ssl_certificate_key /path/to/default.key;
access_log /var/log/nginx/sni_audit.log sni_audit buffer=64k flush=5s;
# 其他 location 配置...
}
-
server_name _;表示兜底匹配,确保所有 SNI 请求都经过该块处理(SNI 匹配由 SSL 层完成,HTTP 层仍需一个通用 server 接收) -
buffer和flush参数提升高并发写入性能,减少 I/O 压力 - 不要在多个 server 块中重复定义相同名称的
access_log,Nginx 会覆盖
分析与审计建议
该日志可用于以下典型场景:
-
多域名分布统计:用
awk -F'|' '{print $3}' sni_audit.log | sort | uniq -c | sort -nr快速查看高频 SNI 域名 -
异常域名筛查:过滤出
$ssl_server_name为空、或包含非法字符(如空格、控制符)、或不在白名单内的域名 -
SNI 与 Host 不一致告警:当
$ssl_server_name != $host,可能表示客户端异常、代理篡改或潜在攻击(如 SNI 欺骗绕过 WAF) -
证书错配定位:结合
$ssl_server_name和$ssl_protocol,快速定位某域名是否因协议降级(如只支持 TLS 1.0)而频繁失败


















