frp 内网穿透实战:把家里的服务安全暴露到公网
家里那台 NAS 存着全家十年的照片,你在公司想拿一份,结果发现运营商给的是大内网地址,光猫上那个 IP 一查是 100.64 开头。DDNS 配了也没用,压根没有公网 IP 可解析。类似场景还有:树莓派上跑着监控面板、家里小主机上有个测试环境想让同事点一下看效果。每次都得用商业穿透的免费版,限速又限量。
frp 做的事一句话能说完:在有公网 IP 的服务器上跑服务端,家里机器上跑客户端,两者保持一条长连接;外面的人访问服务器的某个端口,流量顺着这条已有连接反向送到你内网的机器上,再把响应带回来。不需要路由器做端口映射,只要家里机器能上网就行。
原理先弄清楚,后面配置就不用瞎改
关键是「连接的方向」。很多人第一次接触以为是服务器主动连到你家,那当然不可能。实际是:
- 内网客户端启动后主动向服务器的 7000 端口发起连接,靠心跳保持不断。
- 外部用户访问服务器的某个入口端口或域名。
- 服务器把请求通过那条已建好的连接转发给内网机器。
- 内网机器处理完沿原路返回,服务器再交还给外部用户。
从外部看,服务器像在提供这个服务,实际干活的机器在家里。这也解释了后面两个必踩的坑:为什么服务器端口没放行就完全不通,为什么速度取决于服务器带宽。
服务端配置
先写一份 TOML,存成 /opt/frp/frps.toml:
bindPort = 7000
auth.method = "token"
auth.token = "换成你自己的长随机串"
vhostHTTPPort = 80
vhostHTTPSPort = 443
subDomainHost = "example.com"
webServer.addr = "0.0.0.0"
webServer.port = 7500
webServer.user = "admin"
webServer.password = "换成强密码"
log.to = "/opt/frp/frps.log"
log.level = "info"
log.maxDays = 7
| 配置项 | 作用 | 注意 |
|---|---|---|
| bindPort | 客户端连进来的端口 | 默认 7000,可换非标端口减少扫描 |
| auth.token | 双向认证口令 | 不设等于对全网开放,必须设 |
| vhostHTTPPort | http 类型代理的入口 | 只做纯端口映射时可以不配 |
| subDomainHost | 二级域名后缀 | 配了之后可用子域名自动拼地址 |
| webServer | 网页管理面板 | 看在线客户端和流量,别裸奔在公网 |
用 Docker 起,记得用 host 网络——frp 要按配置动态监听很多端口,bridge 模式得逐个映射,麻烦且容易漏:
mkdir -p /opt/frp
cd /opt/frp
docker run -d \
--name frps \
--restart unless-stopped \
--network host \
-v /opt/frp/frps.toml:/etc/frp/frps.toml \
snowdreamtech/frps
compose 写法:
services:
frps:
image: snowdreamtech/frps
container_name: frps
restart: unless-stopped
network_mode: "host"
volumes:
- ./frps.toml:/etc/frp/frps.toml
起来后要放行:7000(客户端接入)、7500(管理面板,最好只允许自己的 IP)、80 和 443(用 http 类型时)、以及给各服务分配的 remotePort。云服务器记得两处都开:控制台安全组和机器本身的防火墙。
客户端配置
存成 /opt/frp/frpc.toml,一份文件能同时定义多个代理,这是它比图形化工具舒服的地方:
serverAddr = "你的服务器公网IP"
serverPort = 7000
auth.method = "token"
auth.token = "和服务端一致的口令"
log.to = "/opt/frp/frpc.log"
log.level = "info"
# 一、纯端口映射:内网 22 暴露成服务器的 60022
[[proxies]]
name = "home-ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = 60022
# 二、域名映射:内网 8080 暴露成 blog.example.com
[[proxies]]
name = "home-blog"
type = "http"
localIP = "127.0.0.1"
localPort = 8080
customDomains = ["blog.example.com"]
两种类型选错了会很困惑,区别说清楚:
- tcp:服务器上开一个端口,流量原样转发到内网。适合 SSH、远程桌面、数据库这类非 HTTP 协议。缺点是每个服务占一个端口,地址带端口号。
- http:按域名分流,多个服务共用 80 端口,靠 Host 头区分,地址干净。缺点是需要域名解析到服务器。
docker run -d \
--name frpc \
--restart unless-stopped \
--network host \
-v /opt/frp/frpc.toml:/etc/frp/frpc.toml \
snowdreamtech/frpc
如果客户端跑在容器里而服务在同机宿主机上,localIP 不能写 127.0.0.1(那指容器自己),要写宿主机内网 IP 或者用 host 模式。这个坑排查起来特别费时间,日志里看不出任何异常,就是连不到。
开机自启
长连接服务最怕机器重启后忘了手动拉起,出门在外才发现连不上。别用 nohup,交给 systemd,崩了会自动重启。存成 /etc/systemd/system/frpc.service:
[Unit]
Description=frp client
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/bin/frpc -c /opt/frp/frpc.toml
Restart=always
RestartSec=5
LimitNOFILE=1048576
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable --now frpc
systemctl status frpc --no-pager
服务端同理,把启动命令换成服务端二进制和对应配置,文件名改成 frps.service。Restart=always 是关键——网络抖动时 frp 自己会重连,进程万一挂了靠 systemd 兜底。
域名解析和 Nginx 配合
用 http 类型时,域名要解析到服务器 IP,加一条 A 记录就够。外部访问这个域名,服务器 80 端口收到请求,服务端看 Host 头匹配到对应客户端,转发过去。
如果服务器上已经跑了 Nginx 占着 80,就会冲突。两个方案:把 vhostHTTPPort 改成 8080,Nginx 里给这个域名加一条反向代理指向它;或者让 frp 只监听内网地址,流量先经 Nginx 再分。
加反向代理时记得这两行,否则服务端拿到的 Host 不是原始域名,匹配不到规则直接 404:
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
额外好处是 HTTPS 可以统一在 Nginx 上做,证书管理集中在一处。
带宽与并发
穿透后的速度上限等于链路上最窄的那一段:
- 服务器带宽是硬上限。一台 3 Mbps 的小机器,你家宽带再快,通过它访问就是 3 Mbps,跑满后所有连接一起卡。
- 并发数也吃资源。同时几个人拉文件,CPU 和带宽都会见底。
- 你家宽带的上行同样重要,家用宽带上行普遍只有下行十分之一,传大文件时这端先顶不住。
所以远程改配置、看小网页、传几百 KB 的文件,3 Mbps 完全够;拿它当家庭影音中心随时看片就不现实。买服务器时看清楚带宽而不是流量,很多便宜套餐流量给得慷慨,带宽只有 1 到 3 Mbps。
安全:不设防等于把内网交出去
- 一定设 token。没有 token 的服务端,任何人扫到你的端口都能注册代理,把服务器当跳板。用长随机串,两端必须一致。
- 管理面板不要裸奔。带账号密码也最好用防火墙只放行自己的 IP,或者绑到本机地址通过 SSH 隧道访问。
- SSH 映射要有额外限制。把家里的 22 端口映射到公网等于摆出来挨爆破,优先密钥登录、禁用密码登录。
- 用非标端口。7000 和被扫得最凶的那些端口一样是脚本的固定目标,换成五位数能过滤掉大部分噪音。
- 按需开启。不常用的服务别常驻映射,注释掉,需要时再打开,攻击面越少越安全。
常见坑
7000 没放行
客户端日志反复刷连接超时。检查云控制台安全组和本机防火墙两处,经常是漏了其中一处。从客户端机器上探测一下端口,通了再往下查。
改完配置忘了重启
frp 不会热加载。改完必须重启服务,改了不生效然后怀疑配置写错,是最浪费时间的场景。改完顺手看一眼日志有没有加载成功的提示。
http 类型返回 404 或访问到别的站点
多半是域名解析没做好,或者请求到了服务器但 Host 不匹配。确认 A 记录指向这台服务器、浏览器访问的确实是配置的那个域名、反代有正确传 Host 头。用带自定义 Host 头的 curl 请求可以绕过 DNS 直接测。
两端版本不一致
配置文件格式在演进中改过,新老版本混用会出现解析失败,或者连上了但代理不生效。两边用同一个镜像标签或同一批安装包。
小服务器带宽被跑满
典型症状是「一开始能用,几天后突然全卡」,监控里带宽长期跑满。要么是有人扫到端口在拉东西,要么是你自己的相册缩略图、同步工具在持续传输。打开面板看流量,关掉不必要的代理。
被扫描爆破
日志里出现大量陌生 IP 的失败尝试,说明端口被盯上了。应对顺序:换端口 → 加强 token → 防火墙限制来源 → 关掉不需要的映射。
什么情况该用,什么情况别用
适合:需要从外网访问内网某个具体服务、没有公网 IP 又不想折腾运营商、只有一两台设备要穿透、预算就是一台最便宜的小服务器。配置是纯文本,出问题看日志就能定位。
不适合:需要给几十上百人提供稳定的公网服务(那该正经买带宽)、需要企业级身份认证和审计、或者你对 Linux 和防火墙完全不熟又不想学。
起步建议:先用 tcp 类型映射一个 SSH 端口把链路跑通,确认 token、端口、自启都正常,再上 http 配域名。一次只加一个代理,出问题好定位。







