80%的人第一周就放弃,是因为没搞懂这几个参数。
这事最早不是为了折腾技术。家里的绿联 DXP4800 在局域网里用得好好的,可一出门,照片、书库和几个自建服务就全成了“看得见门牌、进不了院子”。宽带又没有固定公网 IP,打电话给运营商问了几次,最后只得到一句很标准的回复:住宅宽带给不了静态地址。
那就上 frp。
听上去不复杂:有公网 IP 的云服务器跑 frps,NAS 跑 frpc,把内网端口转出去。真正把我卡住的,却是一堆看起来不起眼的小参数。前三天我改一次配置、重启一次进程、在手机流量和家里 Wi-Fi 之间来回切换,折腾到最后甚至怀疑是 NAS 网卡坏了。
后来我重新整理配置、进程托管和监控,整套服务连续跑了 8 个月,没有出现需要我手动重连的中断。这不是某一个“神参数”的功劳,而是下面五处最容易漏掉的地方都补上了。
坑一:server_addr 填错,日志却只告诉你连接失败
我最开始把 server_addr 填成了一整串“协议、地址和端口”混在一起的文本。frpc 不会把这一整串当成“服务器地址”,它需要的是主机名或 IP;端口应该单独放进 server_port。
另外两种情况也很常见:
- 把 NAS 自己的局域网 IP 填了进去;
- 把网站端口当成 frps 的通信端口,7000 和 18080 混着用。
能连通的配置,逻辑应该是“地址归地址,端口归端口”。这是我现在使用的 TOML 版本骨架:
serverAddr = "你的服务器地址"
serverPort = 7000
auth.method = "token"
auth.token = "{{ .Envs.FRPC_AUTH_TOKEN }}"
transport.tls.enable = true
# 有自有 CA、需要校验服务端身份时再取消下一行注释
# transport.tls.trustedCaFile = "/etc/frp/tls/ca.crt"
transport.heartbeatInterval = -1
transport.tcpMuxKeepaliveInterval = 30
[[proxies]]
name = "nas-web"
type = "tcp"
localIP = "127.0.0.1"
localPort = 8080
remotePort = 18080这里的 remotePort 才是云服务器对外开放的端口,serverPort 是 frpc 和 frps 握手用的端口,localPort 是 NAS 上真正提供服务的端口。三个端口各司其职,不要看它们都像四位数就顺手填成一样。
改完先校验,再重启:
frpc verify -c /etc/frp/frpc.toml
systemctl restart frpc
journalctl -u frpc -n 100 --no-pager我现在的判断顺序是:serverPort 能从 NAS 连通,说明网络层没跑偏;frpc 日志能完成登录,说明 token 没错;最后才看代理端口是否可用。把故障切成三段,比盯着“connect failed”一行字更有用。
坑二:token 写进配置,又跟着 Git 一起提交
第二个坑更疼。刚开始图省事,直接把 token 写在 frpc.ini 里,还把整个目录推送到了公开代码仓库。虽然后来删了仓库、轮换了密钥,但那几天每次看到提交记录都觉得自己像把家里钥匙挂到了门口。
现在 frp 官方已经说明 INI 格式进入弃用阶段,新项目更适合用 TOML、YAML 或 JSON。TOML 支持从环境变量渲染配置,所以我让 systemd 读取本机的环境文件,配置文件本身可以进仓库:
sudo install -m 600 /dev/null /etc/frp/frpc.env
sudo sh -c 'printf "%s\n" "FRPC_AUTH_TOKEN=换成新生成的随机值" > /etc/frp/frpc.env'/etc/frp/frpc.env 不提交,.gitignore 里同时加上:
.env
*.env
frpc.env如果你还在用很老的 frpc.ini,先确认版本,不要直接照搬 TOML 语法。旧版本没有同样的环境变量模板能力时,可以用启动脚本读取密钥后再生成临时配置,但临时文件仍然要设成 600,并且避免把密钥写进 shell 历史。最省事的办法还是升级 frp,再迁到 TOML。
还有一条经验:token 泄露后不要只改客户端,服务端也要同步轮换。只改一边,最常见的表现就是日志里不断出现 login 失败,然后你在客户端配置里绕半天。
坑三:systemctl start 不等于进程被 systemd 保活
第三个坑持续了三天。我当时的启动方式很“临时”:SSH 连上 NAS,执行一条命令,终端不关就没事;终端一断,进程也跟着没了。后来写了 service 文件,却漏了 enable,NAS 一重启就恢复原样。
一个够用的客户端服务可以这样写:
[Unit]
Description=frp client
After=network-online.target
Wants=network-online.target
StartLimitIntervalSec=0
[Service]
Type=simple
EnvironmentFile=/etc/frp/frpc.env
ExecStart=/usr/local/bin/frpc -c /etc/frp/frpc.toml
Restart=always
RestartSec=5
MemoryMax=512M
LimitNOFILE=65535
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target几个参数不是为了显得专业:
ExecStart必须写绝对路径,最好先用command -v frpc核对;Restart=always负责进程退出后的自动拉起;RestartSec=5避免登录失败时高频重试;EnvironmentFile让密钥和配置分开;systemctl enable frpc才负责开机启动。
启用后别只看一次绿色状态,做一遍完整验证:
sudo systemctl daemon-reload
sudo systemctl enable --now frpc
sudo systemctl is-enabled frpc
sudo systemctl is-active frpc
sudo systemctl restart frpc我的最后一条检查更粗暴:把 NAS 重启一次,等两分钟,再从外网访问。如果只有 is-active 是绿色、重启后却没了,十有八九是把“启动服务”和“设置开机启动”混为一谈。
如果你用的是 Docker 版 frpc,也就不需要硬塞 systemd。把重启策略设为 unless-stopped,同时确认 NAS 的 Docker 服务会在开机后自动启动即可。两套托管方式不要同时开,否则排查时会看到两个进程抢连接。
坑四:TLS 没开,流量特征太明显
这一段我要说得谨慎一点:运营商是否限速、怎么限速,和地区、时段、线路都有关,不能因为一次异常就下结论说“不开 TLS 一定会被 QoS”。
我遇到的情况是,早期那份旧 frpc.ini 没有显式配置 TLS。白天偶尔能连,晚上固定时段开始频繁重连,网页加载一段时间就卡住。换成带 TLS 的连接后,同样线路、同样 NAS、同样端口,稳定性明显改善。这个变化只能证明“我的场景里,加密连接比裸配置更稳”,不能把它当成运营商官方规律。
现在新版本 frp 默认已经开启 TLS,但旧配置和自定义服务端不能想当然。官方文档说明,从 v0.50.0 起 transport.tls.enable 默认为 true;如果你的服务端要求只接受 TLS,可以设置:
# frps.toml
transport.tls.force = true客户端侧再核对:
transport.tls.enable = true如果你要验证服务端身份,还可以把 CA 证书放到客户端,用 transport.tls.trustedCaFile 做单向校验。别一边开启校验,一边随手写着 insecureSkipVerify = true,那不叫加密链路,只是把警报静音了。
另外,frp 的连接加密不等于你穿透出去的网页自动有 HTTPS。frp 和 frps 之间用了 TLS,只解决中间链路;外面的浏览器访问你的服务,仍然要在反向代理层配置自己的证书。
坑五:把虚拟内存当网络缓冲,最后把自己拖垮
这个坑很隐蔽。我看到 frpc 偶尔内存上涨,就把 NAS 的 swap 开得很大,想让它“扛一扛峰值”。结果遇到大文件传输时,系统开始频繁回收内存,磁盘 I/O 拉满,心跳也跟着超时。外面看是“穿透又断了”,实际根因在 NAS 本身。
后来我做了三件事:
- 不再把 swap 当成无限的网络缓冲区;
- 给 frpc 设
MemoryMax=512M,先让异常进程受控,再查它为什么涨; - 用
free -h、vmstat 1和journalctl -u frpc分开看内存、I/O 与连接日志。
free -h
vmstat 1 10
journalctl -u frpc --since "30 min ago" --no-pager“虚拟内存开太大被封”这个说法,我只能确认在自己的环境里出现过系统卡死、连接超时和资源告警,不能倒推成平台统一的处罚规则。更可靠的做法是:控制进程内存、限制并发、把日志和大文件缓存放到合适的位置,再监控 NAS 的 CPU、内存、磁盘等待和代理连接数。swap 是兜底,不是性能优化按钮。
实际跑通后,状态应该长什么样
为了不把家里的公网地址和 NAS 信息暴露出来,我用同一个 frp 版本在本机回环环境做了一次完整联调:本地网页、frpc、frps 和远端代理端口都实际跑起来,然后从代理端口反向访问测试页。
客户端界面里,local-web 代理应显示为运行状态,日志应出现“登录成功”和“代理启动成功”。

服务端同时能看到已连接客户端数和活动代理数,TLS 强制开启等关键配置也能直接核对。

最后再从代理端口访问一次本地页面。这个“页面真的打开了”的动作,比只看客户端一颗绿灯更可靠。

8 个月后,我留下的最小检查表
这套东西能稳定跑,不是因为我每天都在修它,而是刚开始把容易反复出错的地方处理干净了:
serverAddr只填主机名或 IP,端口单独写;- token 不进 Git,文件权限设为
600; - systemd 同时检查
active和enabled,或者统一交给 Docker 托管; - 服务端和客户端都核对 TLS 状态,旧配置不要凭印象;
- 不拿 swap 硬扛流量,限制进程内存并观察系统资源;
- 改配置先
frpc verify,再重启和看最近 100 行日志。
没有公网 IP 确实不方便,但 frp 不是“填完就能一劳永逸”的魔法。它更像一条长期要走的小路:地址、身份、守护、加密和资源边界,少补一块,平时都没事,偏偏在你最需要远程访问的时候出问题。
参数依据
文中的 serverAddr、auth.token、transport.tls.*、心跳和 systemd 用法,均按 frp 官方配置、通信安全、systemd 和客户端参考文档重新核对。不同版本的小版本字段可能略有差异,复制前先用 frpc verify 做本地校验,并以安装版本自带说明为准。


