frp 应该算是我日常折腾服务器时用得最频繁的内网穿透工具,没有之一。它的用途非常直接:一台机器在内网里,没有公网地址,外部网络访问不到,那就用另一台有公网地址的服务器做中转,把内网端口“映射”出去,让外部网络能通过公网服务器访问到内网服务。frp 配置看着不复杂,核心就两个配置文件,一个跑在有公网地址的服务器上,一个跑在内网机器上,但真正花时间的地方几乎都在常见错误排查上——token 对不上、端口被占用、安全组没放行、systemd 起不来、日志里反复出现 EOF 或 connection refused。这篇文章我把 frp 配置和 frp 常见错误放在一起梳理一遍,既能当新手快速上手的文档,也能当排查问题时的参考手册。
1. 先把 frp 的原理和应用场景讲清楚
1.1 frp 到底解决的是一个什么问题
frp 解决的本质上是一个“内网可达性”的问题。假设你在办公室有一台开发机,家里还有一台 NAS,它们只有内网地址,外面的人从公网直接访问不到;而你恰好有一台带公网地址的云服务器,这时候 frp 就能在两者之间搭一条专用的转发通道。外人访问公网服务器的某个端口时,数据会被原样送到你家或者办公室的内网机器上,看起来就像这台内网机器直接暴露在了公网上。
说白了,frp 就是把“公网服务器”当成一个接待前台,所有从外部进来的请求先到达前台,前台再根据登记信息把请求转给真正干活的“内网服务”。这个过程对内网服务是完全透明的,它不会感知到访问流量绕了这么一圈。
理解了这个模型,后面配置和排错就顺了。你会发现很多常见错误其实都是“前台”和“内网服务”之间信息不一致导致的,比如登记地址写错、口令对不上、端口被别的服务抢占了。
1.2 frps 与 frpc 的角色分工
frp 有两个核心角色,名字比较像,但职责完全不同。
frps 是服务端,跑在有公网地址的那台机器上。它负责监听一个固定端口,接受来自客户端的注册请求,也接受来自外部网络的访问请求,然后按配置把流量转到对应的内网目标。
frpc 是客户端,跑在你需要被访问的内网机器上。它主动去连接 frps,把自己本地的某个端口“登记”到服务端,登记完成后,只要服务端的对应端口收到访问,流量就会被转到 frpc 这边的本地服务。
这里最关键的一点是:连接永远是内网机器主动发起的。即便内网机器没有公网地址、处在 NAT 后面,只要它能正常访问外网,就能通过 frpc 把通道建立起来,然后实现外部网络对内部服务的访问。这也是 frp 这类工具适用面广的原因,不需要在路由器上做端口映射,也不需要内网机器真的拥有公网 IP。
1.3 哪些场景适合用 frp
从应用场景看,frp 适合做的工作不外乎这几类:
- 远程 SSH 登录内网服务器,比如在家里连公司内网的开发机。
- 把本地正在开发的 Web 接口暴露出去,给前端、测试同学联调用。
- 在外访问家里的 NAS、路由器管理页面或个人网盘。
- 通过 stcp 这类方式做点对点加密通信,只在两端建立临时访问关系,不把服务直接暴露到公网。
需要提醒的是,frp 更适合临时的、小型的、个人或小团队的使用场景。如果你要做高可用,或者入口要扛大规模流量,建议还是考虑更成熟的负载均衡体系。frp 的定位始终是轻量通道工具,不是流量分发系统,超出了它的边界反而容易把自己坑进去。
2. 开始配置前需要准备哪些基础条件
2.1 一台有公网地址的服务器怎么确认
frp 服务端必须有一台能被外网直接访问的机器,最常见的就是云服务器。你首先要确认这台服务器的公网地址是真实、可用的。
可以通过ip addr看网卡地址,也可以执行curl ifconfig.me查看出口 IP,然后跟云厂商控制台显示的公网 IP 做对比。如果两者不一致,说明机器可能处在云平台内部 NAT 后面,访问还是要看控制台那个公网地址。
另外,提前在纸上记一下后面会用到的端口:frps 接收 frpc 连接的端口(我习惯用 7000)、控制面板端口(比如 7500)、要映射出去的 remotePort(比如 16022)。这样配置时不至于一团乱麻。
2.2 下载安装包与选择合适架构
frp 的发布包放在 GitHub Releases 页面,文件命名一般是frp_版本号_系统_架构.tar.gz。
- Linux x86 服务器,下载
linux_amd64的包。 - 树莓派或 ARM 开发板,下载
linux_arm64或linux_arm。 - mac 本地下调试,下载
darwin_amd64或darwin_arm64。
下载前先用uname -m确认架构,x86_64 对应 amd64,aarch64 对应 arm64,别不看架构就直接解压跑,否则很可能遇到Exec format error这种启动失败。
2.3 frp 目录结构和配置文件初识
解压之后你会看到 frps、frpc、以及带.toml后缀的配置模板文件。这里要特别提醒一点:frp 在较新的版本里已经统一使用 TOML 配置格式,默认读取frps.toml和frpc.toml。网上很多老教程还在用frps.ini、frpc.ini,那些内容看看思路就好,直接抄配置在新版本上很可能会解析失败。
我习惯把二进制和配置都放到固定目录,方便后面用 systemd 管理:
mkdir -p /opt/frp tar -xzf frp_0.61.1_linux_amd64.tar.gz -C /opt/frp --strip-components=1 cd /opt/frp ls -l正常情况下能看到frps、frpc和对应的.toml文件。目录路径后面写 systemd service 时要保持一致,不要一会儿用/root/frp,一会儿用/opt/frp。
3. 服务端 frps 配置实操
3.1 最小可运行配置
先编辑服务端配置/opt/frp/frps.toml,写一个最小可用版本:
bindPort = 7000 auth.method = "token" auth.token = "改成你自己的复杂随机字符串"bindPort是 frp 服务端用来接收 frpc 连接的端口,可以按需要调整,但后面客户端必须保持一致。auth.token是两端握手的口令,建议用随机生成的长字符串,不要用123456这种一眼能猜出来的弱口令。
这个配置其实已经能跑起来,但实际使用中我会直接把控制面板一起打开,方便观察连接状态。
3.2 控制面板与端口规划
控制面板是 frp 自带的 Web 状态页,能看到当前有哪些客户端在线、哪些通道正在工作。配置长这样:
bindPort = 7000 auth.method = "token" auth.token = "改成你自己的复杂随机字符串" webServer.addr = "0.0.0.0" webServer.port = 7500 webServer.user = "admin" webServer.password = "请改成强密码"控制面板端口没有特殊要求,只要不和 bindPort 冲突即可。需要注意的是,面板带登录口令,默认账号密码一定要改,尤其是当webServer.addr配成0.0.0.0的时候,外部网络也能访问到面板。
我自己实际使用时会把这个端口加来源 IP 限制,只允许办公网络 IP 访问,不给公网裸奔的机会。
3.3 用 systemd 把 frps 做成常驻服务
直接用./frps -c frps.toml前台启动只适合调试。真正要长时间跑,我建议放到 systemd 里管理,这样服务器重启后能自动拉起,进程意外退出也会按策略重启。
创建一个 systemd 服务文件:
cat > /etc/systemd/system/frps.service <<'EOF' [Unit] Description=frp server After=network.target [Service] Type=simple ExecStart=/opt/frp/frps -c /opt/frp/frps.toml Restart=on-failure RestartSec=5s [Install] WantedBy=multi-user.target EOF然后执行:
systemctl daemon-reload systemctl enable --now frps systemctl status frps看到active (running)就说明服务端已经起来了。后面改配置后记得执行systemctl restart frps。
4. 客户端 frpc 配置实操
4.1 最常用的 TCP 端口映射配置
假设我想在内网机器上把本机的 22 端口映射到公网服务器的 16022 端口,frpc.toml可以这样写:
serverAddr = "你的服务器公网IP" serverPort = 7000 auth.method = "token" auth.token = "和 frps.toml 保持一致" [[proxies]] name = "ssh" type = "tcp" localIP = "127.0.0.1" localPort = 22 remotePort = 16022字段解释一下:
serverAddr和serverPort:frps 的地址和连接端口,端口必须和frps.toml的bindPort一致。[[proxies]]:定义一个转发条目,name可以自己取,但不能和同一客户端下其他条目重名。type:转发类型,TCP 转发就写tcp。localIP和localPort:内网实际服务的地址和端口。如果服务就在本机,写127.0.0.1没问题;如果 frpc 要转发同一局域网里另一台机器的服务,这里要写那台机器的内网 IP。remotePort:frps 对外提供服务要使用的端口,这个端口要保证在公网服务器上没有被其他服务占用。
启动客户端:
./frpc -c frpc.toml启动后,在任意一台有公网访问能力的机器上执行ssh user@服务器公网IP -p 16022,就能登录到内网机器。
注意:
remotePort不能用服务器上已占用的端口,否则绑定时会直接失败。如果服务器是云厂商的,16022 这个端口还必须在安全组里放行,否则外部流量到不了 frps。
4.2 其他常用转发类型:UDP、HTTP、STCP
UDP 转发和 TCP 差别不大,把type改成udp,其余逻辑一样。适合做 DNS 调试、NTP 同步这类场景的外网访问。
HTTP 转发则更适合有域名的情况。frps 上需要开启vhostHTTPPort,frpc 里配置type = "http"和customDomains = ["app.example.com"],服务端收到 HTTP 请求后会按照域名把流量转发到对应的内网服务。这样做的好处是不需要为每个 Web 服务分配独立端口,一个 HTTP 端口加不同域名就能承载多个内网站点。
STCP 不一样,它不直接暴露对外端口,而是通过 frps 做信令交换,由两个 frpc 之间建立点对点加密通道。适合想对外安全提供服务又不想让端口直接暴露在公网上的场景,配置上也多一层 visitor 概念。
我第一次用 frp 的时候,直接先从最简单的 TCP 转发开始,跑通之后再尝试 HTTP、STCP。建议你也按这个顺序来,不然一堆配置项堆在一起出问题时,根本不知道从哪里排查起。
4.3 客户端日志与启动验证
frpc 前台启动后,正常情况下日志里会出现类似start proxy success的字样,并打印出当前映射的地址信息。如果看到login to server failed,那就是和第 6 章的常见错误对上了。
客户端同样推荐用 systemd 管理,避免重启服务器之后 frpc 没有自动拉起来。配置文件路径和启动命令换成 frpc 对应版本即可。
5. 配置完成后的联调与网络放行
5.1 从零开始的启动顺序
frp 的启动顺序其实没有强依赖,frpc 先启动也能等 frps 恢复后自动连上,但我习惯先确保 frps 服务端起来并监听端口,再启动 frpc,这样日志比较干净,方便定位问题。
真正需要重点检查的反而是配置一致性:服务端bindPort和客户端serverPort是否一致,两边 token 是否完全一致,remotePort是否冲突。顺序对了但配置对不上,一样连不通。
5.2 验证内外网是否真正打通
验证分三步走:
- 在服务器上执行
ss -lntp | grep 16022,确认 frps 已经在监听对应端口。 - 打开 frps 控制面板,在浏览器访问
http://服务器公网IP:7500,登录后看到客户端在线、通道状态正常。 - 在另一台外部机器上执行
telnet 服务器公网IP 16022,或者直接用业务命令验证,比如 SSH 端口就直接ssh -p 16022 user@服务器公网IP。
如果服务器本机访问端口是通的,但外部不通,那问题八成出在网络放行这一环。
5.3 防火墙与云平台安全组注意事项
服务器自带的防火墙(比如 firewalld 或 ufw)和云平台的安全组是两个独立环节,任何一个挡着都会失败。
常见现象是:frpc 日志显示连接成功,控制面板也显示在线,但外部访问超时。这种情况十有八九是安全组或服务器防火墙没放行 remotePort。排查时可以先用curl -v或者telnet从外部访问,再看是超时还是拒绝,超时大概率是安全组丢包,拒绝则可能是端口没监听。
开放防火墙端口示例:
firewall-cmd --permanent --add-port=7000/tcp firewall-cmd --permanent --add-port=7500/tcp firewall-cmd --permanent --add-port=16022/tcp firewall-cmd --reload如果用的是 ufw:
ufw allow 7000/tcp ufw allow 7500/tcp ufw allow 16022/tcp云平台安全组的操作不在命令行里,每家的控制台位置不同,但思路一样:找到安全组规则,添加入方向的对应端口放行规则。我经常看到有人改了 frp 配置半天,最后发现只是安全组漏了一条规则,特别浪费时间。
6. frp 常见错误排查实战
6.1 配置文件解析错误导致启动失败
现象:启动 frps 或 frpc 时直接报invalid config或者load config error。
排查思路:
- 先确认配置文件后缀是
.toml,新版本已经不推荐使用旧的.ini后缀。 - 再看字段名是否按 TOML 规范写,比如
serverAddr是驼峰写法,不是旧版的server_addr。 - 检查是否有中文引号、中文冒号混在配置文件里。这个问题常见于在 Windows 编辑器中复制内容再粘贴到 Linux 的场景。
- 较新版本提供了配置校验命令,强烈建议启动前先跑一遍:
./frpc verify -c frpc.toml服务端对应使用./frps verify -c frps.toml。校验能通过,再谈其他排错。
6.2 token 认证失败与 401 报错
现象:客户端日志出现login to server failed: 401或者auth error。
原因几乎都是两端 token 不一致。先对比frpc.toml和frps.toml的 token 是否完全一样,注意不要有空格、不要用中文引号。如果服务端开了auth.method = "token",而客户端没有写这个字段,也可能导致认证异常。
我遇到过最气人的情况是,token 复制的时候从日志里多复制了一个换行符进去,肉眼完全看不出来,但两边就是握不上。所以建议token 写成无空格、无换行的纯字符串。
6.3 bind 端口被占用的问题
现象:frps 启动时日志出现port already used,或者服务起不来。
排查时先在服务器上执行:
ss -lntp | grep 7000如果看到其他进程占了 7000 端口,要么换端口,要么把占用进程处理掉。另一种常见情况是配置了多个代理,但把多个remotePort配成了一样的值,导致后定义的代理无法绑定。日志里通常会有类似proxy name duplicated或者端口绑定的报错,看到后挨个检查每个转发条目的remotePort即可。
6.4 客户端已经连上,但外部始终访问不了
这是实际使用中频率最高的一个问题。
分三步排查:
- 在服务器本机执行
telnet 127.0.0.1 16022或curl 127.0.0.1:16022,看端口是否在监听。如果本机都连不通,说明 frps 没有成功监听,或者转发通道没有注册成功。 - 如果本机能通,但外部机器不行,去检查安全组和服务器防火墙。
- 如果外部能连到端口,但连接后马上就断,去看后端内网服务的
localIP和localPort填得对不对,内网服务本身有没有在监听。
还有一种隐蔽情况:服务器公网 IP 写错了。尤其是云服务器有内网 IP 和公网 IP 两个地址,frpc 的serverAddr必须填公网 IP,填了内网 IP 只会导致外部通道永远建立不起来。
6.5 EOF、timeout、connection refused 分别代表什么
日志里出现这些关键字,含义完全不同:
EOF:连接被对方正常或异常关闭。常见于 token 不正确,服务端主动断掉握手;也可能是服务端重启导致旧连接没被清理,客户端重连后恢复。timeout:网络不通或端口被丢包。重点检查安全组、防火墙、frps 的bindPort是否监听。connection refused:端口没有监听。常见于remotePort被占用,或者 frps 还没有起来。
看到这些关键词不用慌,先把它对应到具体嫌疑点,再逐项排除。
6.6 常见错误速查表
把上面这些问题整理成一张表格,遇到问题先对着表格查,能省不少时间:
| 现象 | 可能原因 | 优先排查动作 |
|---|---|---|
| 启动报 invalid config | TOML 格式或字段名写错 | 执行frpc verify -c frpc.toml |
| 日志出现 401 | token 不一致 | 对比两端 token 是否完全一致 |
| bind 端口失败 | 端口被其他服务占用 | 执行ss -lntp查看端口占用 |
| 客户端在线但外部不能访问 | 安全组或防火墙未放行 | 检查云平台安全组和本机防火墙 |
| 连接后立刻断开 / EOF | token 错误或服务端拒绝 | 查看 frps 端日志 |
| timeout | 网络不通或端口被丢包 | 检查公网 IP、防火墙、安全组 |
| connection refused | 端口未监听 | 确认 frps 是否正常运行,端口是否被占 |
6.7 几个容易被忽略的低级错误
最后再单独列出几个我见过很多次的低级错误:
- 用中文双引号写配置文件,导致 TOML 解析失败。
- 手动复制 token 时,把日志里的省略号或者换行符一起复制进去。
- frpc 连接公网服务器时,IP 填成了云服务器内网 IP。
- 修改配置文件后忘记重启 frp 服务,还在用旧配置跑。
这些错误排查起来不难,但容易在忙乱中反复踩。我的习惯是每次改完配置,先执行一遍verify,再重启服务,最后看日志确认状态。
7. 一些提升使用体验的小细节
7.1 版本选择与升级思路
不要盲目追最新版,也不要用太老的版本。frp 迭代速度很快,部分版本之间协议或配置格式有调整,升级前一定先备份现有配置,再替换二进制文件,升级后在本地环境先跑通再更新线上。
版本太老的问题主要是新特性没有,协议兼容也可能出问题;版本太新的问题则是社区资料还不充分,遇到问题能搜到的案例少。一般我会选择最近半年内发布的功能相对完整的版本。
7.2 日志策略与磁盘占用
frp 如果通过 systemd 管理,日志默认会进入 journald。长时间运行后,日志量不可小觑,尤其是有大量连接请求和控制面板刷新的情况。
可以用下面的命令查看服务日志:
journalctl -u frps -n 50 --no-pager journalctl -u frpc -f如果担心日志占用过多磁盘,可以在 frps 的 systemd service 里加日志清理相关配置,或者定期用journalctl --vacuum-size=200M限制日志体积。简单场景下,我通常只保留最新的一部分日志,重点是能看到最近几次启动和报错信息。
7.3 关于 token 和安全习惯
frp 的 token 本质上是两端握手时的口令,务必用高强度随机字符串,不要把 token 提交到代码仓库、不要写在公开笔记里。
控制面板的账号密码也一定要改默认值,不要用默认的admin。如果服务器只给固定来源使用,尽量在防火墙层面限制来源 IP。
从个人习惯来说,我会把服务端 systemd 服务、防火墙放行命令、配置文件模板整理成一套固定流程,换新机器的时候直接复用。frpc 每新增一个转发通道都会在配置里加注释,说明这个通道是给哪个服务用的、对应内网端口是什么。这样配置多了之后不会混乱,排查问题的时候也知道每一个条目当初为什么加。
最后再分享一个小技巧:遇到 frp 连不上、访问异常这类问题,先别急着改配置,先把 frps 和 frpc 两端的日志同时打开,对比看是在握手阶段就断了,还是握手成功后转发阶段才出问题。这样做能直接砍掉一大半排查路径,比盲试配置高效得多。