frp 内网穿透实战:把家里的服务安全暴露到公网

admin 开源软件 2026-10-20 5 0

家里那台 NAS 存着全家十年的照片,你在公司想拿一份,结果发现运营商给的是大内网地址,光猫上那个 IP 一查是 100.64 开头。DDNS 配了也没用,压根没有公网 IP 可解析。类似场景还有:树莓派上跑着监控面板、家里小主机上有个测试环境想让同事点一下看效果。每次都得用商业穿透的免费版,限速又限量。

frp 做的事一句话能说完:在有公网 IP 的服务器上跑服务端,家里机器上跑客户端,两者保持一条长连接;外面的人访问服务器的某个端口,流量顺着这条已有连接反向送到你内网的机器上,再把响应带回来。不需要路由器做端口映射,只要家里机器能上网就行。

原理先弄清楚,后面配置就不用瞎改

关键是「连接的方向」。很多人第一次接触以为是服务器主动连到你家,那当然不可能。实际是:

  1. 内网客户端启动后主动向服务器的 7000 端口发起连接,靠心跳保持不断。
  2. 外部用户访问服务器的某个入口端口或域名。
  3. 服务器把请求通过那条已建好的连接转发给内网机器。
  4. 内网机器处理完沿原路返回,服务器再交还给外部用户。

从外部看,服务器像在提供这个服务,实际干活的机器在家里。这也解释了后面两个必踩的坑:为什么服务器端口没放行就完全不通,为什么速度取决于服务器带宽。

服务端配置

先写一份 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双向认证口令不设等于对全网开放,必须设
vhostHTTPPorthttp 类型代理的入口只做纯端口映射时可以不配
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。

安全:不设防等于把内网交出去

  1. 一定设 token。没有 token 的服务端,任何人扫到你的端口都能注册代理,把服务器当跳板。用长随机串,两端必须一致。
  2. 管理面板不要裸奔。带账号密码也最好用防火墙只放行自己的 IP,或者绑到本机地址通过 SSH 隧道访问。
  3. SSH 映射要有额外限制。把家里的 22 端口映射到公网等于摆出来挨爆破,优先密钥登录、禁用密码登录。
  4. 用非标端口。7000 和被扫得最凶的那些端口一样是脚本的固定目标,换成五位数能过滤掉大部分噪音。
  5. 按需开启。不常用的服务别常驻映射,注释掉,需要时再打开,攻击面越少越安全。

常见坑

7000 没放行

客户端日志反复刷连接超时。检查云控制台安全组和本机防火墙两处,经常是漏了其中一处。从客户端机器上探测一下端口,通了再往下查。

改完配置忘了重启

frp 不会热加载。改完必须重启服务,改了不生效然后怀疑配置写错,是最浪费时间的场景。改完顺手看一眼日志有没有加载成功的提示。

http 类型返回 404 或访问到别的站点

多半是域名解析没做好,或者请求到了服务器但 Host 不匹配。确认 A 记录指向这台服务器、浏览器访问的确实是配置的那个域名、反代有正确传 Host 头。用带自定义 Host 头的 curl 请求可以绕过 DNS 直接测。

两端版本不一致

配置文件格式在演进中改过,新老版本混用会出现解析失败,或者连上了但代理不生效。两边用同一个镜像标签或同一批安装包。

小服务器带宽被跑满

典型症状是「一开始能用,几天后突然全卡」,监控里带宽长期跑满。要么是有人扫到端口在拉东西,要么是你自己的相册缩略图、同步工具在持续传输。打开面板看流量,关掉不必要的代理。

被扫描爆破

日志里出现大量陌生 IP 的失败尝试,说明端口被盯上了。应对顺序:换端口 → 加强 token → 防火墙限制来源 → 关掉不需要的映射。

什么情况该用,什么情况别用

适合:需要从外网访问内网某个具体服务、没有公网 IP 又不想折腾运营商、只有一两台设备要穿透、预算就是一台最便宜的小服务器。配置是纯文本,出问题看日志就能定位。

不适合:需要给几十上百人提供稳定的公网服务(那该正经买带宽)、需要企业级身份认证和审计、或者你对 Linux 和防火墙完全不熟又不想学。

起步建议:先用 tcp 类型映射一个 SSH 端口把链路跑通,确认 token、端口、自启都正常,再上 http 配域名。一次只加一个代理,出问题好定位。