frp 80/443 Web 服务两种部署模式实操笔记

环境:frp 最新版,Nginx‑1.26.3,宝塔面板多站点环境;目标:公网域名穿透获取访客真实客户端 IP。 两种模式:frp vhost 七层插件模式(http2http /https2https)、全量 TCP 透传模式 (type=tcp)。

一、Vhost 七层插件模式(http2http /https2https)

工作原理

frps 对外监听 80、443,完成 HTTP/HTTPS 协议解析、域名路由;frpc 通过插件做七层转发。 frpc 内部作为 HTTP 客户端访问本机 Nginx 127.0.0.1:80/443,在 HTTP 请求头自动插入 X‑Forwarded‑For,携带公网用户真实 IP 。 Nginx 读取该 HTTP 头,通过 real_ip 模块,把真实 IP 赋值给 $remote_addr,PHP $_SERVER['REMOTE_ADDR'] 获取真实访客 IP。

本模式 frps 需要开启 vhost 端口,frpc 不需要填写transport.proxyProtocolVersion。

frps.toml(服务端)

复制代码
bindAddr = "0.0.0.0"
bindPort = 7000
auth.method = "token"
auth.token = "你的token"

# 开启vhost,七层模式必须
vhostHTTPPort = 80
vhostHTTPSPort = 443

frpc.toml(客户端)

Dart 复制代码
# HTTP 80 vhost转发
[[proxies]]
name = "web‑http"
type = "http"
customDomains = ["xxx.com"]#你的网站域名
plugin = "http2http"
plugin.localAddr = "127.0.0.1:80"

# HTTPS 443 vhost转发
[[proxies]]
name = "web‑https"
type = "https"
customDomains = ["xxx.com"]#你的网站域名
plugin = "https2https"
plugin.localAddr = "127.0.0.1:443"
plugin.localScheme = "https"

Nginx 配置(宝塔最佳实践)

不需要修改任何listen行,不要写proxy_protocol标记 。 将 real_ip 配置写在 nginx 主配置 nginx.conf 的http{}全局块,全部站点自动继承,无需逐个修改网站虚拟主机文件。

复制代码
http {
    #frpc vhost 获取真实IP‑start
    set_real_ip_from 127.0.0.1;
    real_ip_header X‑Forwarded‑For;
    real_ip_recursive on;
    #frpc vhost 获取真实IP‑end

    # 原有nginx配置保持不变
    ......
    include /www/server/panel/vhost/nginx/*.conf;
}

校验重载:

复制代码
nginx -t
nginx -s reload

模式特点

  1. ✅内网可以直接访问服务器内网 IP 的 80/443 端口调试网站,连接不会重置;
  2. ✅宝塔多站点友好,全局配置一次,所有网站生效;
  3. ✅支持 HTTP3‑QUIC;
  4. ✅PHP $_SERVER['REMOTE_ADDR']拿到真实公网 IP;
  5. ✅证书可以放在内网 Nginx;也可以放在 frps;
  6. ⚠️IP 信息存储在 HTTP Header X‑Forwarded‑For;
  7. ✅安全:set_real_ip_from 127.0.0.1,仅信任 frpc 本机传入的请求头,公网无法直连 Nginx,无法伪造 IP 头。

二、TCP 全透传模式 type=tcp(配合 Proxy‑Protocol v2)

工作原理

frps 直接转发原始 TCP 字节流,frps、frpc 不解析 HTTP/TLS,证书、SNI 全部保留在内网 Nginx 。 frpc 连接本地 Nginx 建立 TCP 连接完成后,TCP 连接首包发送 Proxy‑Protocol v2 二进制报文 ,报文二进制内部携带客户端真实公网 IP,不是 HTTP 头。 Nginx 通过listen xxx proxy_protocol;解析二进制 PP 报文,再通过 real_ip 模块将解析出来的 IP 覆盖$remote_addr。

⚠️重要:transport.proxyProtocolVersion = "v2" 只写在 frpc.toml,frps.toml 不能写该字段,会报 json: unknown field "proxyProtocolVersion"。

frps.toml(服务端)

复制代码
bindAddr = "0.0.0.0"
bindPort = 7000
auth.method = "token"
auth.token = "你的token"

# TCP透传模式,必须注释/删除vhost端口
#vhostHTTPPort = 80
#vhostHTTPSPort = 443

frpc.toml(客户端)

复制代码
# TCP透传80
[[proxies]]
name = "tcp‑80"
type = "tcp"
remotePort = 80
localIP = "127.0.0.1"
localPort = 80
transport.proxyProtocolVersion = "v2"

# TCP透传443
[[proxies]]
name = "tcp‑443"
type = "tcp"
remotePort = 443
localIP = "127.0.0.1"
localPort = 443
transport.proxyProtocolVersion = "v2"

Nginx 配置限制与写法

  1. proxy_protocol 是 listen 套接字标记,同一个 ip:port 下所有 server {} 的 listen 参数必须完全一致,全部开启或全部关闭;开源 Nginx 没有全局开关。
  2. QUIC/UDP 443 不支持 proxy_protocol,listen 443 quic; 禁止追加该参数。

示例单站点配置:

Dart 复制代码
server {
    listen 80 proxy_protocol;
    listen 443 ssl proxy_protocol;
    listen 443 quic; # UDP QUIC保持原样

    server_name xxx.com;#你的网站域名

    #frpc tcp透传获取真实ip‑start
    set_real_ip_from 127.0.0.1;
    real_ip_header proxy_protocol;
    real_ip_recursive on;
    #frpc tcp透传获取真实ip‑end
}

TCP 模式致命问题:内网直接访问服务器 80/443 端口连接重置

现象:开启listen 80 proxy_protocol;之后,公网 frp 访问正常;公司内网直接访问服务器内网 IP:80,浏览器提示连接已重置。

原因

listen 80 proxy_protocol;标记告诉 Nginx:该 socket 上所有 TCP 连接第一个数据包必须是 Proxy‑Protocol v2 二进制报文。

  • 公网流量走 frp:frpc 会发送 PP‑v2 二进制首包,Nginx 正常解析处理。
  • 内网同事直接访问服务器内网 IP:浏览器发出普通 HTTP 数据包,没有 PP‑v2 二进制头。Nginx 收到非预期数据,直接关闭 TCP 连接,表现为连接重置。

开启proxy_protocol的监听端口,不再接受普通浏览器客户端直连,仅允许上游代理设备(frpc/haproxy)接入。

解决方案
  1. 方案一(推荐):放弃 80/443 使用 tcp 透传,切回 vhost 七层插件模式,内网访问不受任何影响。
  2. 方案二:修改端口,不直接占用 80/443 frps 不占用 80、443,web 业务改用 vhost 模式;纯 TCP 业务(RDP、数据库等)使用其他 remotePort,这类业务本来就不会被内网浏览器直接访问,不会触发连接重置问题。

示例:RDP 转发

复制代码
[[proxies]]
name = "rdp"
type = "tcp"
remotePort = 13389
localPort = 3389
transport.proxyProtocolVersion = "v2"
  1. 方案三(架构复杂,不适合宝塔):本机增加一层 HAProxy 做流量分流,内网流量直接放行,frpc 流量传入带 proxy‑protocol,再转发给 Nginx。宝塔原生环境不建议实施。

补充:宝塔多站点场景下,使用 tcp 80/443 透传会遇到额外难题:开源 nginx 强制全部虚拟主机 listen 参数完全一致,任意网站漏写proxy_protocol,会出现a duplicate listen 0.0.0.0:80配置启动报错,维护成本极高。

三、两种模式完整对比表

表格

对比项 Vhost 七层插件模式 http2http/https2https TCP 全透传 type=tcp + Proxy‑Protocol v2
IP 携带位置 HTTP Header X‑Forwarded‑For TCP 连接首包二进制 Proxy‑Protocol v2 报文
nginx real_ip_header real_ip_header X‑Forwarded‑For; real_ip_header proxy_protocol;
frps 配置 开启vhostHTTPPort、vhostHTTPSPort 注释关闭 vhost 系列端口
frpc 配置 plugin 插件,无transport.proxyProtocolVersion type=tcp,配置transport.proxyProtocolVersion = "v2",仅 frpc 配置
Nginx listen 行 不需要修改,不加 proxy_protocol 所有 TCP 80/443 ssl 的 listen 必须追加proxy_protocol;
内网直接访问服务器 IP:80/443 ✅正常访问,无连接重置 ❌直接连接重置,普通客户端不能直连该端口
宝塔多站点适配 ✅极好,http 全局写一次 real_ip 即可 ❌很差,全部站点 listen 参数必须统一,极易启动报错
HTTP3‑QUIC 支持 ✅正常支持 QUIC/UDP 不走 tcp 隧道,公网无法使用
证书存放位置 frps 侧 / 内网 Nginx 均可 证书必须全部放在内网 Nginx,frp 不处理 TLS 解密
适用场景 网站 Web 业务,同时需要内网调试访问 纯 TCP 业务 (RDP、数据库);后端端口只给代理访问,禁止普通客户端直连

四、最终选型建议

  1. Web 网页 80/443 业务优先选择 vhost 七层插件模式 。兼顾公网真实 IP 获取、内网调试访问,适配宝塔多站点,维护简单稳定。虽然 IP 来自 HTTP 头,但set_real_ip_from 127.0.0.1限制信任来源,安全等级足够。
  2. RDP、数据库等纯 TCP 业务,使用 type=tcp + Proxy‑Protocol v2,使用非 80/443 的 remotePort,避开 web 端口冲突,不会产生内网直连重置问题。
  3. 不建议在宝塔环境把 80/443 网页服务直接使用 type=tcp 全透传模式,会带来内网访问失效、Nginx 配置维护负担。

核心总结:Proxy‑Protocol v2 能力很强,但附带硬性约束:开启该标记的端口只能接收代理的流量,普通浏览器不能直连,鱼和熊掌不可兼得。

相关推荐
xier_ran3 分钟前
【infra之路】AI 编译器学习笔记(二):重拾 C++——移动语义与 LLVM 风格的类型转换
人工智能·笔记·学习
Dovis(誓平步青云)8 分钟前
常用命令藏得太深?怎么做一个快捷命令坞
运维·前端·自动化·音视频·prometheus
IT_陈寒1 小时前
Vite的这个问题坑了我三天,原来如此解决!
前端·人工智能·后端
网易云信1 小时前
大模型能上岗做销售吗?我们让 12 款国产模型交了 420 份作业
前端·后端
蜗牛互联网1 小时前
Python Agent工具调用的白名单、参数校验与审批门禁
java·服务器·前端·人工智能·后端·python
计算机魔术师1 小时前
Perplexity:每个 Project 都是一个 Git 仓库
前端
Andrew5512 小时前
让 AI 写页面不再“一眼假”:我把设计品味做成了一个 Claude Code Skill 流水线
前端
乐享qicheng2 小时前
14-AI智能管家JSBridge深度问答
前端
10年前端老司机2 小时前
我用 Vue3 + LangGraph 做了个心理咨询 AI,踩了 9 个坑后总结出来的实战经验
前端·langchain·agent
数据掘金2 小时前
A/B 实验分组参数回滚后还在生效?一次参数校验体系的重构复盘
前端