单公网IP下基于SNI实现多服务的TCP/HTTP Reverse Proxy配置咨询
单公网IP下基于SNI实现多服务的TCP/HTTP Reverse Proxy配置咨询
你的思路方向是完全正确的——用Nginx的stream模块结合SNI实现TCP层的域名转发,确实能在单公网IP下区分不同服务的流量。不过你当前配置里把80和443放在同一个stream server块里是有问题的,这里给你拆解问题并给出优化方案:
核心问题:80端口无法用SNI区分流量
SNI是HTTPS(443端口)特有的机制,依赖SSL握手阶段传递服务器名称;而80端口是明文HTTP,没有SSL握手过程,ssl_preread根本读不到任何SNI信息,所以直接把80监听放在stream块里,无法区分不同域名的请求。
正确的配置方案:拆分80/443端口处理
我们需要把443端口的TCP流量(依赖SNI)和80端口的HTTP流量(依赖Host头)分开处理:
1. 443端口:用stream模块做SNI导向的TCP转发
这部分保留你原本的核心逻辑,只监听443端口,通过ssl_preread读取SNI后转发到对应上游服务:
stream { # 映射SNI域名到对应的上游集群 map $ssl_preread_server_name $target_upstream { serviceone.domain.tld serviceone; servicetwo.domain.tld servicetwo; website1.domain.tld website1_https; website2.domain.tld website2_https; } # 定义各服务的上游地址 upstream serviceone { server 10.1.99.7:443; # 直接透传TCP到服务的443端口 } upstream servicetwo { server 10.1.99.20:443; } upstream website1_https { server 10.1.99.30:10443; # 网站内部的HTTPS监听端口 } upstream website2_https { server 10.1.99.30:10444; } server { listen 443; ssl_preread on; # 开启SNI预读取(不终止SSL连接,只读取SNI) proxy_pass $target_upstream; resolver 8.8.8.8 valid=300s; # 缓存DNS解析结果,提升性能 } }
2. 80端口:用http模块做Host头导向的转发/跳转
80端口的HTTP请求自带Host头,我们可以直接用Nginx的http模块处理,同时兼顾Let's Encrypt的ACME验证需求:
http { # 为每个域名单独配置server块,更清晰易维护 server { listen 80; server_name serviceone.domain.tld; # 处理Let's Encrypt的证书验证请求 location /.well-known/acme-challenge/ { root /var/www/letsencrypt; # 提前创建此目录并赋予Nginx权限 try_files $uri =404; } # 这类需要TCP代理的服务通常不需要HTTP访问,直接跳转到HTTPS location / { return 301 https://$host$request_uri; } } server { listen 80; server_name servicetwo.domain.tld; location /.well-known/acme-challenge/ { root /var/www/letsencrypt; try_files $uri =404; } location / { return 301 https://$host$request_uri; } } server { listen 80; server_name website1.domain.tld; location /.well-known/acme-challenge/ { root /var/www/letsencrypt; try_files $uri =404; } # 转发HTTP请求到网站内部的HTTP端口(也可以直接跳转到HTTPS) location / { proxy_pass http://10.1.99.30:1080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } server { listen 80; server_name website2.domain.tld; location /.well-known/acme-challenge/ { root /var/www/letsencrypt; try_files $uri =404; } location / { proxy_pass http://10.1.99.30:1081; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } }
额外优化建议
如果你的网站不需要保留原始SSL会话(比如可以在Nginx层终止HTTPS),可以把网站的443流量也放到http模块处理,这样能统一管理SSL证书,不用每个网站单独部署证书:
http { server { listen 443 ssl; server_name website1.domain.tld; ssl_certificate /path/to/website1.fullchain.pem; ssl_certificate_key /path/to/website1.privkey.pem; location / { proxy_pass http://10.1.99.30:1080; # 其他代理头配置 } } }
这时候stream模块只需要处理serviceone和servicetwo的443 TCP流量即可,配置会更简洁。
总结
你的核心思路没问题,只要把80和443端口的处理逻辑拆分,就能完美实现单公网IP下多服务的域名导向转发,同时满足TCP代理服务、网站访问和Let's Encrypt证书验证的需求。
备注:内容来源于stack exchange,提问作者Blair
相关产品推荐
相关产品推荐

