You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

单公网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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.21 13:28:07