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

相关推荐
子兮曰7 小时前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰7 小时前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
一隅论数智7 小时前
给AI一张“业务概念地图“:本体如何从哲学走向企业智能
大数据·人工智能·经验分享·笔记·学习·学习方法·政务
前端小万8 小时前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝8 小时前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋8 小时前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁9 小时前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
深圳老胡10 小时前
STM32F407 控制 L6470 步进电机驱动 —— 控制过程简介
笔记·stm32·单片机·嵌入式硬件·代码规范
Because_of_Her110 小时前
并查集-听课笔记
笔记·算法·并查集
彧azz10 小时前
Linux 环境下 Redis 学习总结:数据类型、持久化、锁、事务、主从与缓存问题
linux·redis·笔记·学习·面试