Nginx做反向代理时,大部分人熟悉的是七层的http模块配SSL,也就是通过listen 443 ssl加ssl_certificate指令给网站加HTTPS。但如果要代理的是MySQL、Redis、Kafka这类基于TCP的自定义协议,七层模块就无能为力了,这时候需要stream模块在四层做转发。而四层流量同样可能需要加密,ngx_stream_ssl_module就是专门负责这件事的模块。它提供了一套与http层相似但不完全相同的SSL指令集,支持服务端证书、双向认证、协议版本控制、会话缓存等能力。

一、模块原理与编译加载方式
ngx_stream_ssl_module并不是默认编译进Nginx的模块。如果你使用的是官方发布的源码包自行编译,需要在configure阶段显式加上--with-stream_ssl_module参数,同时还依赖--with-stream这个基础模块。完整的编译参数示例如下:
./configure --with-stream --with-stream_ssl_module \
--with-http_ssl_module \
--with-openssl=/usr/local/src/openssl-3.0.0
make && make install编译完成后,可以通过nginx -V查看已有的编译参数,确认输出中包含stream_ssl_module字样。如果使用的是各大Linux发行版的Nginx包,例如通过apt或yum安装的nginx,一般会以动态模块形式提供,检查/usr/lib/nginx/modules目录下是否存在ngx_stream_ssl_module.so即可。若是以动态模块方式加载,需要在nginx.conf最顶部使用load_module指令引入,注意这条指令必须写在所有其他指令之前。
从工作原理上看,stream层的SSL终结发生在TCP连接建立之后、数据转发之前。客户端发起TCP握手,Nginx完成SSL握手后,如果配置了ssl参数,后续的报文会被解密再转发给上游。这和LVS这类纯四层转发有本质区别,Nginx在四层代理中依然可以理解TLS协议本身,只是不理解TLS载荷里的应用层协议内容。
二、基础配置:在stream块中启用SSL
stream模块的配置必须写在http块之外、与http块平级的stream块中。一个最基础的服务端SSL配置如下:
stream {
upstream mysql_backend {
server 192.168.1.10:3306;
server 192.168.1.11:3306;
}
server {
listen 3306 ssl; # ssl参数表示该端口启用TLS
proxy_pass mysql_backend;
# 证书配置
ssl_certificate /etc/nginx/certs/server.pem;
ssl_certificate_key /etc/nginx/certs/server.key;
# 协议版本控制
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
}
}有几个细节需要注意。第一,listen指令后的ssl参数是开关,没有它证书配置不会生效,客户端用普通TCP连接也能进来,这是新手最常踩的坑之一。第二,证书路径建议使用PEM格式,如果私钥有密码,Nginx启动时会交互式询问,生产环境通常用无密码私钥配合文件权限管理。第三,ssl_session_cache设置为shared模式可以在多个worker进程间共享会话,避免每个进程各自缓存导致命中率低下。
TLSv1.3相比TLSv1.2在握手性能上有明显优势,1-RTT甚至0-RTT的握手减少了往返次数,对四层长连接场景帮助很大。但如果客户端软件较老,比如某些嵌入式设备只支持TLSv1.0,就要谨慎裁剪ssl_protocols列表,在安全性和兼容性之间做权衡。
三、进阶:客户端证书校验与上游加密
单向TLS只验证服务端身份,如果业务要求客户端也必须证明自己是合法的,比如内网数据库代理只允许特定应用服务器连接,就需要开启双向认证。stream模块同样支持ssl_verify_client指令:
server {
listen 6379 ssl;
proxy_pass redis_backend;
ssl_certificate /etc/nginx/certs/server.pem;
ssl_certificate_key /etc/nginx/certs/server.key;
# 双向认证配置
ssl_client_certificate /etc/nginx/certs/ca.pem;
ssl_verify_client on;
ssl_verify_depth 2;
}配置后,客户端连接时必须携带由指定CA签发的证书,否则SSL握手阶段就会被拒绝,连TCP层面的数据都不会传输。这在安全上是好事,握手即失败,不会暴露任何应用层信息。需要注意的是,ssl_client_certificate指向的是CA证书而非客户端证书本身,验证的是证书链的信任关系。
另一个常见需求是Nginx作为SSL终结点后,与上游服务器之间的链路也要加密。这时需要用到proxy_ssl系列指令,这部分属于ngx_stream_proxy_module,但与SSL配置紧密相关:
server {
listen 443 ssl;
proxy_pass kafka_backend:9093;
ssl_certificate /etc/nginx/certs/server.pem;
ssl_certificate_key /etc/nginx/certs/server.key;
# 到上游的连接也走TLS
proxy_ssl on;
proxy_ssl_certificate /etc/nginx/certs/client.pem;
proxy_ssl_certificate_key /etc/nginx/certs/client.key;
proxy_ssl_trusted_certificate /etc/nginx/certs/upstream-ca.pem;
proxy_ssl_verify on;
proxy_ssl_server_name on;
}这样就形成了一条客户端到Nginx、Nginx到上游的双重加密链路。如果上游是自签名证书,要么把证书加入信任列表,要么临时关闭proxy_ssl_verify,但生产环境强烈不建议关闭验证,中间人攻击的风险不可忽视。
四、常见报错排查与性能调优
配置完成后,排错是绕不开的环节。最典型的报错是no ssl_certificate is defined,说明server块里加了ssl参数却忘了配证书。另一个是握手失败时日志里出现SSL_do_handshake() failed,常见原因包括协议版本不匹配、密码套件不支持、客户端证书过期等。排查时建议把error_log的级别调到debug,配合openssl s_client -connect host:port命令从客户端侧模拟握手,能快速定位问题出在哪一步。
性能方面有几点值得优化。会话复用方面,除了前面提到的shared缓存,TLSv1.3默认支持会话票据,可以进一步减少握手开销。硬件层面,如果流量很大,确认Nginx链接的是支持硬件加速的OpenSSL版本,AES-NI指令集能显著降低加解密CPU占用。另外,ssl_handshake_timeout默认60秒偏长,公网环境建议缩短到10秒左右,防止恶意慢速握手拖垮连接资源。还可以通过ssl_session_tickets和定期轮换证书密钥来平衡性能与安全性。最后提醒一点,修改stream相关配置后用nginx -t测试语法再reload,因为stream块配置错误同样会导致reload失败,影响整个Nginx服务的平滑运行。
Nginxngx_stream_ssl_module流SSL加密修改时间:2026-09-16 18:00:46