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.confhttp{}全局块,全部站点自动继承,无需逐个修改网站虚拟主机文件。

复制代码
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 能力很强,但附带硬性约束:开启该标记的端口只能接收代理的流量,普通浏览器不能直连,鱼和熊掌不可兼得。

相关推荐
熊猫钓鱼>_>1 小时前
鸿蒙AI Agent新范式:从“对话式辅助”到“工程化代理”的Harness架构实战解析
人工智能·笔记·学习·华为·架构·harmonyos
JamesZhang800781 小时前
Chrome相关知识点
前端
GGMM7891 小时前
西门子 AAOP 亚洲高级操作面板详解(MM420/MM440 专用)
笔记·变频器·变频器维修
jayson.h1 小时前
python——pdf编辑
前端·python·pdf
GGMM7891 小时前
西门子 IOP 智能操作面板详细使用教程(G120/G120C)
笔记·变频器·变频器维修
顶级自由人1 小时前
本地正常、线上正常,为什么一个 Hook 仍会报错?
前端·javascript·程序员
Android小行家1 小时前
Android APK 加固原理(五):SO `.text` 段加密、ELF 加载与运行时动态解密
前端
lerhxx1 小时前
R3F 第一人称漫游与碰撞检测:Pointer Lock + 不穿墙的滑墙秘诀(中)
前端·javascript·three.js
LSCLikeApple2 小时前
入门promise
前端