在使用 frp 的情况下,如何使用同一端口,不同子域名,反代内网的不同服务呢? 不使用 frp 的 vhost 服务。
思路一句话:把 frp 当成“带 Host 路由的 TCP 隧道”来用,而别去碰它专门给 HTTP(S) 准备的 vhost 端口。
具体做法就是用 tcpmux + httpconnect 模式:frps 只开一个普通端口(例如 7000)和一个 tcpmux 端口(例如 5002),frpc 里给每个子域名建一条 tcpmux 代理;frps 收到 CONNECT 请求后按 Host 头转发到对应的内网服务。全程不走 vhostHTTPPort/vhostHTTPSPort,也就绕开了 frp 自带的 vhost 功能。
1. frps(服务端)
frps.toml
bindPort = 7000 # 控制通道
tcpmuxHTTPConnectPort = 5002 # 唯一对外端口,所有子域名都连这里2. frpc(客户端,每套服务一份)
例:把 git.example.com 转发到内网 192.168.1.10:22,db.example.com 转发到 192.168.1.20:3306
frpc-git.toml
serverAddr = x.x.x.x
serverPort = 7000
[[proxies]]
name = "git-ssh"
type = "tcpmux"
multiplexer = "httpconnect"
customDomains = ["git.example.com"]
localIP = "192.168.1.10"
localPort = 22frpc-db.toml
serverAddr = x.x.x.x
serverPort = 7000
[[proxies]]
name = "db-mysql"
type = "tcpmux"
multiplexer = "httpconnect"
customDomains = ["db.example.com"]
localIP = "192.168.1.20"
localPort = 33063. DNS
把 *.example.com 解析到 frps 的公网 IP 即可。
4. 客户端怎么连
任何支持 HTTP CONNECT 的客户端都能用,统一指向 5002 端口,Host 头决定落到哪台内网机器。
- SSH 示例(用 socat 当 CONNECT 代理)
ssh -o ProxyCommand='socat - PROXY:frps公网IP:%h:22,proxyport=5002' user@git.example.com- MySQL 示例
mysql --protocol=tcp -h db.example.com -P 3306 -u user -p \
--ssl-mode=DISABLED \
--default-auth=mysql_native_password(MySQL 8 客户端默认走 SSL,先关掉 SSL 才能走纯 TCP 隧道)
要点回顾
- 全程只用 5002 一个端口,不同子域名靠 Host 区分。
- 没开 vhostHTTPPort/vhostHTTPSPort,也没用到 http/https 代理类型,完全符合“不用 frp vhost”的要求。
如果我使用 nginx 反代,那么该如何配置呢/
frp 只负责把“带 Host 头的 TCP 流”原封不动地送到 Nginx,Nginx 再按子域名反向代理到真正的内网业务。
这样——
- 公网端口只用 frps 的 tcpmux 端口(下面示例用 7001);
- 不启用 frp 的 vhost_http(s)_port;
- 所有七层路由都由 Nginx 做,frp 仅当“透明隧道”。
一、frps(服务端)
frps.toml
bindPort = 7000 # 控制通道
tcpmuxHTTPConnectPort = 7001 # 唯一对外端口二、frpc(客户端)
把 7001 收到的流全部转给“内网 Nginx”的 80 端口(或 443,如果你打算在 Nginx 里统一终结 TLS)。
frpc.toml
serverAddr = x.x.x.x
serverPort = 7000
[[proxies]]
name = "nginx-http"
type = "tcpmux"
multiplexer = "httpconnect"
customDomains = ["*.example.com"] # 也可以逐条写
localIP = "192.168.1.2" # 内网 Nginx 地址
localPort = 80三、Nginx(跑在内网 192.168.1.2)
- 先建一个“默认”server,专门接收 frp 隧道过来的流量:
# /etc/nginx/sites-available/frp-tunnel
server {
listen 80;
server_name _; # 匹配所有 Host
# 如果只想让 frp 过来的流量进这个 server,可再加
# allow 192.168.0.0/16; deny all;
# 按子域名分发
include /etc/nginx/conf.d/subdomain_*.conf;
}- 给每个子域名写独立配置(方便管理):
/etc/nginx/conf.d/subdomain_git.conf
server {
listen 80;
server_name git.example.com;
location / {
proxy_pass http://192.168.1.10:3000; # 真正的 GitLab/Gitea
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}/etc/nginx/conf.d/subdomain_db.conf
server {
listen 80;
server_name db.example.com;
location / {
proxy_pass http://192.168.1.20:8080; # 数据库 Web 控制台
proxy_set_header Host $host;
...
}
}- 如需 HTTPS,统一在 Nginx 里做 TLS 终结:
server {
listen 443 ssl http2;
server_name git.example.com;
ssl_certificate /etc/nginx/ssl/git.example.com.crt;
ssl_certificate_key /etc/nginx/ssl/git.example.com.key;
location / {
proxy_pass http://192.168.1.10:3000;
...
}
}frp 隧道依旧走 80,Nginx 内部 80→443 重定向即可。
四、DNS
*.example.com A 记录 → 公网 IP(frps 所在机器)。
五、客户端访问
浏览器直接 https://git.example.com 或 http://db.example.com,流量路径:
用户 → 7001(tcpmux) → frps → frpc → Nginx(80/443) → 真正的内网服务。
六、小结
- 公网只暴露 7001,子域名数量无限制;
- frp 不参与七层路由,完全由 Nginx 按 server_name 分发;
- 升级/新增子域名只需在 Nginx 里加配置,frp 端零改动。