frp内网穿透
实现内网穿透的工具有很多,比如ngrok、frp等,这里介绍使用frp实现内网穿透。
注意 文档要看英文文档,英文文档才比较全。
frp 内网穿透完整配置指南
前提条件检查
- 公网服务器(VPS/云服务器)
- 域名(例如:abc.com)
- 本地服务,如Docker部署Keycloak 端口 8081
- 服务器 SSH 访问权限
- 域名 DNS 管理权限
- SSL 证书(可选,推荐)
完整实施流程
服务器端配置
- SSH 登录服务器
ssh root@your-server-ip- 下载并安装 frps(服务端)
创建目录
$ mkdir frp
$ cd frp下载最新版 frp(检查最新版本:https://github.com/fatedier/frp/releases)
$ wget https://github.com/fatedier/frp/releases/download/v0.65.0/frp_0.65.0_linux_amd64.tar.gz
--2025-12-23 14:21:44-- https://github.com/fatedier/frp/releases/download/v0.65.0/frp_0.65.0_linux_amd64.tar.gz
正在解析主机 github.com (github.com)... 20.205.243.166
正在连接 github.com (github.com)|20.205.243.166|:443... 已连接。
已发出 HTTP 请求,正在等待回应... 302 Found
位置:https://release-assets.githubusercontent.com/github-production-release-asset/48378947/64bf462b-03f7-4f27-9364-05576dff36ca?sp=r&sv=2018-11-09&sr=b&spr=https&se=2025-12-23T06%3A59%3A38Z&rscd=attachment%3B+filename%3Dfrp_0.65.0_linux_amd64.tar.gz&rsct=application%2Foctet-stream&skoid=96c2d410-5711-43a1-aedd-ab1947aa7ab0&sktid=398a6654-997b-47e9-b12b-9515b896b4de&skt=2025-12-23T05%3A59%3A03Z&ske=2025-12-23T06%3A59%3A38Z&sks=b&skv=2018-11-09&sig=bjuYd2qw7Gd%2FqReB7%2BPaZ2c%2BkWQxx8ugbGh9S8Zm7E8%3D&jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmVsZWFzZS1hc3NldHMuZ2l0aHVidXNlcmNvbnRlbnQuY29tIiwia2V5Ijoia2V5MSIsImV4cCI6MTc2NjQ3MjcwNCwibmJmIjoxNzY2NDcwOTA0LCJwYXRoIjoicmVsZWFzZWFzc2V0cHJvZHVjdGlvbi5ibG9iLmNvcmUud2luZG93cy5uZXQifQ.Xmvdcr-RatEXTRmPhyve_Om667AWSBmWiGV-6-MibYc&response-content-disposition=attachment%3B%20filename%3Dfrp_0.65.0_linux_amd64.tar.gz&response-content-type=application%2Foctet-stream [跟随至新的 URL]
--2025-12-23 14:21:44-- https://release-assets.githubusercontent.com/github-production-release-asset/48378947/64bf462b-03f7-4f27-9364-05576dff36ca?sp=r&sv=2018-11-09&sr=b&spr=https&se=2025-12-23T06%3A59%3A38Z&rscd=attachment%3B+filename%3Dfrp_0.65.0_linux_amd64.tar.gz&rsct=application%2Foctet-stream&skoid=96c2d410-5711-43a1-aedd-ab1947aa7ab0&sktid=398a6654-997b-47e9-b12b-9515b896b4de&skt=2025-12-23T05%3A59%3A03Z&ske=2025-12-23T06%3A59%3A38Z&sks=b&skv=2018-11-09&sig=bjuYd2qw7Gd%2FqReB7%2BPaZ2c%2BkWQxx8ugbGh9S8Zm7E8%3D&jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmVsZWFzZS1hc3NldHMuZ2l0aHVidXNlcmNvbnRlbnQuY29tIiwia2V5Ijoia2V5MSIsImV4cCI6MTc2NjQ3MjcwNCwibmJmIjoxNzY2NDcwOTA0LCJwYXRoIjoicmVsZWFzZWFzc2V0cHJvZHVjdGlvbi5ibG9iLmNvcmUud2luZG93cy5uZXQifQ.Xmvdcr-RatEXTRmPhyve_Om667AWSBmWiGV-6-MibYc&response-content-disposition=attachment%3B%20filename%3Dfrp_0.65.0_linux_amd64.tar.gz&response-content-type=application%2Foctet-stream
正在解析主机 release-assets.githubusercontent.com (release-assets.githubusercontent.com)... 185.199.109.133, 185.199.110.133, 185.199.111.133, ...
正在连接 release-assets.githubusercontent.com (release-assets.githubusercontent.com)|185.199.109.133|:443... 已连接。
已发出 HTTP 请求,正在等待回应... 200 OK
长度:13808813 (13M) [application/octet-stream]
正在保存至: “frp_0.65.0_linux_amd64.tar.gz”
frp_0.65.0_linux_amd64.tar.gz 100%[===========================================================================================>] 13.17M 12.7KB/s 用时 12m 12s
2025-12-23 14:33:57 (18.4 KB/s) - 已保存 “frp_0.65.0_linux_amd64.tar.gz” [13808813/13808813])
$ ls
frp_0.65.0_linux_amd64.tar.gzwget默认下载到当前目录
解压
$ tar -xzf frp_0.65.0_linux_amd64.tar.gz
$ ls
frp_0.65.0_linux_amd64 frp_0.65.0_linux_amd64.tar.gz
$ cd frp_0.65.0_linux_amd64
$ ls
frpc frpc.toml frps frps.toml LICENSE也可以在本地下载后使用scp上传到服务器,其他上传方案也是可以的:
$ scp ./frps.toml tdy-aliyun:/root/matiastang/frp/
$ scp ./frps tdy-aliyun:/root/matiastang/frp/
# ...(上传进度输出省略)可通过arch命令查看系统架构,选择对应的发行版,如frp_0.65.0_linux_amd64.tar.gz:
$ arch
x86_64复制服务端程序到系统路径,推荐的做法但不是必须,可以是任何路径下,如果不在/usr/local/bin/下,需要指定路径启动
$ cp frps /usr/local/bin/
$ chmod +x /usr/local/bin/frps- 创建日志目录
$ mkdir -p /var/log/frp- 创建 frps 配置文件
创建配置目录
$ mkdir -p /etc/frp查询端口是否被占用
$ lsof -i :7000编辑配置文件/etc/frp/frps.toml
# frps.toml - frp 服务端配置
# 基础配置
bindPort = 7000 # frp 服务端口
vhostHTTPPort = 80 # HTTP 虚拟主机端口
vhostHTTPSPort = 443 # HTTPS 虚拟主机端口
# Web 管理面板(可选但推荐)
webServer.addr = "0.0.0.0"
webServer.port = 7500
webServer.user = "admin"
webServer.password = "change_me_please" # ⚠️ 请修改密码
# 安全配置
auth.method = "token"
auth.token = "your_secure_token_here" # ⚠️ 请设置一个强密码
# 日志配置
log.to = "/var/log/frp/frps.log"
log.level = "info"
log.maxDays = 7
# 性能配置
transport.maxPoolCount = 5
transport.tcpMux = true重要:请修改以下配置:
- webServer.password: 改为强密码
- auth.token: 改为一个复杂的随机字符串(至少 32 位)
配置使用之前记得验证一下,是否正常:
$ ./frps verify -c frps.toml- 创建 systemd 服务文件
在 Linux 系统下,使用 systemd 可以方便地控制 frps 服务端的启动、停止、配置后台运行以及开机自启动。如果系统没有安装 systemd,可先执行:apt install systemd。
$ cd /etc/systemd/system
$ pwd
/etc/systemd/system
$ vim frps.service
[Unit]
Description=Frp Server Service
After=network.target
Wants=network.target
[Service]
Type=simple
User=root
Restart=on-failure
RestartSec=5s
ExecStart=/usr/local/bin/frps -c /etc/frp/frps.toml
ExecReload=/bin/kill -HUP $MAINPID
LimitNOFILE=1048576
[Install]
WantedBy=multi-user.target- 启动 frps 服务
重新加载 systemd
$ systemctl daemon-reload启动 frps
$ systemctl start frps设置开机自启
$ systemctl enable frps查看状态
$ systemctl status frps暂停服务
$ systemctl stop frps
$ systemctl status frps
● frps.service - Frp Server Service
Loaded: loaded (/etc/systemd/system/frps.service; disabled; vendor preset: disabled)
Active: inactive (dead)
12月 23 15:07:09 iZwz968l7cazo3lhnqenz2Z systemd[1]: frps.service: Main process exited, code=exited, status=1/FAILURE
12月 23 15:07:09 iZwz968l7cazo3lhnqenz2Z systemd[1]: frps.service: Failed with result 'exit-code'.
12月 23 15:07:14 iZwz968l7cazo3lhnqenz2Z systemd[1]: frps.service: Service RestartSec=5s expired, scheduling restart.
12月 23 15:07:14 iZwz968l7cazo3lhnqenz2Z systemd[1]: frps.service: Scheduled restart job, restart counter is at 87.
12月 23 15:07:14 iZwz968l7cazo3lhnqenz2Z systemd[1]: Stopped Frp Server Service.
12月 23 15:07:14 iZwz968l7cazo3lhnqenz2Z systemd[1]: Started Frp Server Service.
12月 23 15:07:14 iZwz968l7cazo3lhnqenz2Z frps[2901003]: create vhost http listener error, listen tcp 0.0.0.0:80: bind: address already in use
12月 23 15:07:14 iZwz968l7cazo3lhnqenz2Z systemd[1]: frps.service: Main process exited, code=exited, status=1/FAILURE
12月 23 15:07:14 iZwz968l7cazo3lhnqenz2Z systemd[1]: frps.service: Failed with result 'exit-code'.
12月 23 15:07:17 iZwz968l7cazo3lhnqenz2Z systemd[1]: Stopped Frp Server Service.这里启动失败,因为端口已经被nignx占用了,所以我们只能换一个端口,然后使用nginx代理一下。
查看日志
$ journalctl -u frps -f
-- Logs begin at Sun 2025-06-29 15:34:18 CST. --
12月 23 15:02:49 iZwz968l7cazo3lhnqenz2Z frps[2899999]: create vhost http listener error, listen tcp 0.0.0.0:80: bind: address already in use
12月 23 15:02:49 iZwz968l7cazo3lhnqenz2Z systemd[1]: frps.service: Main process exited, code=exited, status=1/FAILURE
12月 23 15:02:49 iZwz968l7cazo3lhnqenz2Z systemd[1]: frps.service: Failed with result 'exit-code'.
12月 23 15:02:54 iZwz968l7cazo3lhnqenz2Z systemd[1]: frps.service: Service RestartSec=5s expired, scheduling restart.
12月 23 15:02:54 iZwz968l7cazo3lhnqenz2Z systemd[1]: frps.service: Scheduled restart job, restart counter is at 38.
12月 23 15:02:54 iZwz968l7cazo3lhnqenz2Z systemd[1]: Stopped Frp Server Service.期望看到:
● frps.service - Frp Server Service
Loaded: loaded (/etc/systemd/system/frps.service; enabled)
Active: active (running) since ...
frps[xxxxx]: [I] [service.go:xxx] frps started successfully
frps[xxxxx]: [I] [service.go:xxx] dashboard listen on 0.0.0.0:7500添加一个脚本,用于管理frps服务
$ cat frpsmanager.sh
#!/bin/sh
# frps service control helper
SERVICE_NAME="${SERVICE_NAME:-frps}"
print_usage() {
cat <<'EOF'
用法:
frpsmanager.sh start 启动服务
frpsmanager.sh stop 停止服务
frpsmanager.sh restart 重启服务
frpsmanager.sh status 查看服务状态
frpsmanager.sh enable 设置开机启动
frpsmanager.sh disable 取消开机启动
frpsmanager.sh is-enabled 查看是否已开机启动
frpsmanager.sh help 查看帮助
可选环境变量:
SERVICE_NAME 服务名称,默认 frps
示例:
SERVICE_NAME=frps frpsmanager.sh status
EOF
}
need_root_or_sudo() {
if [ "$(id -u)" -ne 0 ]; then
if command -v sudo >/dev/null 2>&1; then
echo "将使用 sudo 执行 systemctl 操作..."
SUDO="sudo"
else
echo "需要 root 权限或 sudo"
exit 1
fi
else
SUDO=""
fi
}
do_systemctl() {
need_root_or_sudo
$SUDO systemctl "$@"
}
case "$1" in
start)
do_systemctl start "$SERVICE_NAME"
;;
stop)
do_systemctl stop "$SERVICE_NAME"
;;
restart)
do_systemctl restart "$SERVICE_NAME"
;;
status)
systemctl status "$SERVICE_NAME"
;;
enable)
do_systemctl enable "$SERVICE_NAME"
;;
disable)
do_systemctl disable "$SERVICE_NAME"
;;
is-enabled)
systemctl is-enabled "$SERVICE_NAME"
;;
help|-h|--help|"")
print_usage
;;
*)
echo "未知命令: $1"
echo
print_usage
exit 1
;;
esac$ chmod +x frpsmanager.sh添加开机启动测试一下
$ ./frpsmanager.sh enable
Created symlink /etc/systemd/system/multi-user.target.wants/frps.service → /etc/systemd/system/frps.service.- 配置防火墙
# 如果使用 UFW
ufw allow 7000/tcp # frp 通信端口
ufw allow 80/tcp # HTTP
ufw allow 443/tcp # HTTPS
ufw allow 7500/tcp # Web 管理面板(可选,建议只允许特定 IP)
# 如果使用 firewalld
firewall-cmd --permanent --add-port=7000/tcp
firewall-cmd --permanent --add-port=80/tcp
firewall-cmd --permanent --add-port=443/tcp
firewall-cmd --permanent --add-port=7500/tcp
firewall-cmd --reload
# 如果使用云服务商安全组
# 需要在云控制台添加相应的入站规则- nignx代理
查看端口是否被占用
$ lsof -i :7080
$ lsof -i :7443修改配置文件
$ vim frps.toml
$ cat frps.toml
# frps.toml - frp 服务端配置
# 基础配置
bindPort = 7000 # frp 服务端口
vhostHTTPPort = 7080 # HTTP 虚拟主机端口
vhostHTTPSPort = 7443 # HTTPS 虚拟主机端口
# Web 管理面板(可选但推荐)
webServer.addr = "0.0.0.0"
webServer.port = 7500
webServer.user = "admin"
webServer.password = "change_me_please" # ⚠️ 请修改密码
# 安全配置
auth.method = "token"
auth.token = "your_secure_token_here" # ⚠️ 请设置一个强密码
# 日志配置
log.to = "/var/log/frp/frps.log"
log.level = "info"
log.maxDays = 7
# 性能配置
transport.maxPoolCount = 3
transport.tcpMux = true再次启动服务
$ systemctl start frps
$ systemctl status frps
● frps.service - Frp Server Service
Loaded: loaded (/etc/systemd/system/frps.service; disabled; vendor preset: disabled)
Active: active (running) since Tue 2025-12-23 15:15:01 CST; 11s ago
Main PID: 2901875 (frps)
Tasks: 10 (limit: 200073)
Memory: 19.5M
CGroup: /system.slice/frps.service
└─2901875 /usr/local/bin/frps -c /etc/frp/frps.toml
12月 23 15:15:01 iZwz968l7cazo3lhnqenz2Z systemd[1]: Started Frp Server Service.配置nginx
$ cd /etc/nginx/conf.d
$ vim frp.conf
$ cat frp.conf
...
# HTTP 服务:用于 Let's Encrypt 验证 + 重定向到 HTTPS
server {
listen 80;
#请填写绑定证书的域名
server_name frp.abc.com;
# Let's Encrypt 验证路径(certbot 会自动处理)
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
#把http的域名请求转成https
#return 301 https://$host$request_uri;
# 👇 其他所有请求才重定向到 HTTPS
location / {
return 301 https://$host$request_uri;
}
}$ nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
$ nginx -s reload
$ certbot certonly \
> --webroot \
> -w /var/www/certbot \
> -d frp.abc.com \
> --email 邮箱 \
> --agree-tos \
> --non-interactive
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for frp.abc.com
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/frp.abc.com/fullchain.pem
Key is saved at: /etc/letsencrypt/live/frp.abc.com/privkey.pem
This certificate expires on 2026-03-23.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
* Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
* Donating to EFF: https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -查看日志
$ tail -f /var/log/frp/frps.log
2025-12-23 15:07:08.986 [I] [frps/root.go:108] frps uses config file: /etc/frp/frps.toml
2025-12-23 15:07:09.178 [I] [server/service.go:236] frps tcp listen on 0.0.0.0:7000
2025-12-23 15:07:14.236 [I] [frps/root.go:108] frps uses config file: /etc/frp/frps.toml
2025-12-23 15:07:14.285 [I] [server/service.go:236] frps tcp listen on 0.0.0.0:7000
2025-12-23 15:15:01.526 [I] [frps/root.go:108] frps uses config file: /etc/frp/frps.toml
2025-12-23 15:15:01.628 [I] [server/service.go:236] frps tcp listen on 0.0.0.0:7000
2025-12-23 15:15:01.628 [I] [server/service.go:304] http service listen on 0.0.0.0:7080
2025-12-23 15:15:01.628 [I] [server/service.go:318] https service listen on 0.0.0.0:7443
2025-12-23 15:15:01.628 [I] [frps/root.go:117] frps started successfully
2025-12-23 15:15:01.628 [I] [server/service.go:350] dashboard listen on 0.0.0.0:7500
$ tail -50 /var/log/nginx/error.log
$ tail -20 /var/log/nginx/access.log更新nginx配置文件
$ cat frp.conf
#HTTPS使用SSL访问的配置
server {
#SSL 默认访问端口号为 443
listen 443 ssl;
#请填写绑定证书的域名
server_name frp.abc.com;
#请填写证书文件的相对路径或绝对路径
ssl_certificate /etc/letsencrypt/live/frp.abc.com/fullchain.pem;
#请填写私钥文件的相对路径或绝对路径
ssl_certificate_key /etc/letsencrypt/live/frp.abc.com/privkey.pem;
#缓存SSL握手产生的参数和加密密钥的时长
ssl_session_timeout 5m;
#请按照以下协议配置
ssl_protocols TLSv1.2 TLSv1.3;
#请按照以下套件配置,配置加密套件,写法遵循 openssl 标准。
#ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256;
#启用 SSL 会话缓存,避免每次 HTTPS 请求都进行完整的 TLS 握手(耗时且消耗 CPU)
ssl_session_cache shared:SSL:10m;
#加密套件优先选择服务器的加密套件
ssl_prefer_server_ciphers off;
# frp 管理页面
location /frp-admin/ {
# IP 白名单(替换为你的公网 IP)
allow 171.223.196.63; # 你的 IP
deny all;
# 👇 关键:使用 rewrite 剥离前缀
rewrite ^/frp-admin/(.*) /$1 break;
proxy_pass http://127.0.0.1:7500; # 👈 注意:末尾不要加 /
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
# 基本认证
auth_basic "FRP Admin Panel";
auth_basic_user_file /etc/nginx/.htpasswd;
}
# frp 主服务(HTTPS 隧道)
location / {
proxy_pass http://127.0.0.1:7443;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
# HTTP 服务:用于 Let's Encrypt 验证 + 重定向到 HTTPS
server {
listen 80;
#请填写绑定证书的域名
server_name frp.abc.com;
# Let's Encrypt 验证路径(certbot 会自动处理)
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
#把http的域名请求转成https
#return 301 https://$host$request_uri;
# 👇 其他所有请求才重定向到 HTTPS
location / {
return 301 https://$host$request_uri;
}
}其中的下面配置:
# 基本认证
auth_basic "FRP Admin Panel";
auth_basic_user_file /etc/nginx/.htpasswd;添加 nginx 认证
$ yum install -y httpd-tools
$ htpasswd -c /etc/nginx/.htpasswd admin
New password:
Re-type new password:
Adding password for user admin验证文件
$ ls -la /etc/nginx/.htpasswd
-rw-r--r-- 1 root root 44 12月 23 15:54 /etc/nginx/.htpasswd
$ cat /etc/nginx/.htpasswd
admin:******访问https://frp.abc.com/frp-admin/,输入用户名和密码即可访问 frp 管理页面。但是由于我们添加的一个前缀frp-admin,所以静态资源的访问有问题。这种一个域名下配置两个服务的配置比较麻烦,所以建议使用两个域名,一个用于 frp 管理页面,一个用于 frp 服务。
注意 这里代理的是/,如果是某个前缀/web/的web服务,打包的资源就需要注意,也需要添加前缀。如果是后端服务就不需要这么处理。
于是完整的nginx配置如下:
frp.conf的配置如下:
$ cat frp.conf
#HTTPS使用SSL访问的配置
server {
#SSL 默认访问端口号为 443
listen 443 ssl;
#请填写绑定证书的域名
server_name frp.abc.com;
#请填写证书文件的相对路径或绝对路径
ssl_certificate /etc/letsencrypt/live/frp.abc.com/fullchain.pem;
#请填写私钥文件的相对路径或绝对路径
ssl_certificate_key /etc/letsencrypt/live/frp.abc.com/privkey.pem;
#缓存SSL握手产生的参数和加密密钥的时长
ssl_session_timeout 5m;
#请按照以下协议配置
ssl_protocols TLSv1.2 TLSv1.3;
#请按照以下套件配置,配置加密套件,写法遵循 openssl 标准。
#ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256;
#启用 SSL 会话缓存,避免每次 HTTPS 请求都进行完整的 TLS 握手(耗时且消耗 CPU)
ssl_session_cache shared:SSL:10m;
#加密套件优先选择服务器的加密套件
ssl_prefer_server_ciphers off;
# frp 主服务(HTTPS 隧道)
location / {
proxy_pass http://127.0.0.1:7443;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
# HTTP 服务:用于 Let's Encrypt 验证 + 重定向到 HTTPS
server {
listen 80;
#请填写绑定证书的域名
server_name frp.abc.com;
# Let's Encrypt 验证路径(certbot 会自动处理)
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
#把http的域名请求转成https
#return 301 https://$host$request_uri;
# 👇 其他所有请求才重定向到 HTTPS
location / {
return 301 https://$host$request_uri;
}
}frpadmin.conf配置如下:
$ cat frpadmin.conf
#HTTPS使用SSL访问的配置
server {
#SSL 默认访问端口号为 443
listen 443 ssl;
#请填写绑定证书的域名
server_name frpadmin.abc.com;
#请填写证书文件的相对路径或绝对路径
ssl_certificate /etc/letsencrypt/live/frpadmin.abc.com/fullchain.pem;
#请填写私钥文件的相对路径或绝对路径
ssl_certificate_key /etc/letsencrypt/live/frpadmin.abc.com/privkey.pem;
#缓存SSL握手产生的参数和加密密钥的时长
ssl_session_timeout 5m;
#请按照以下协议配置
ssl_protocols TLSv1.2 TLSv1.3;
#请按照以下套件配置,配置加密套件,写法遵循 openssl 标准。
#ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256;
#启用 SSL 会话缓存,避免每次 HTTPS 请求都进行完整的 TLS 握手(耗时且消耗 CPU)
ssl_session_cache shared:SSL:10m;
#加密套件优先选择服务器的加密套件
ssl_prefer_server_ciphers off;
# frp 管理页面
location / {
# IP 白名单(替换为你的公网 IP)
allow 171.223.196.63; # 你的 IP
deny all;
proxy_pass http://127.0.0.1:7500/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
# 基本认证
auth_basic "FRP Admin Panel";
auth_basic_user_file /etc/nginx/.htpasswd;
}
}
# HTTP 服务:用于 Let's Encrypt 验证 + 重定向到 HTTPS
server {
listen 80;
#请填写绑定证书的域名
server_name frpadmin.abc.com;
# Let's Encrypt 验证路径(certbot 会自动处理)
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
#把http的域名请求转成https
#return 301 https://$host$request_uri;
# 👇 其他所有请求才重定向到 HTTPS
location / {
return 301 https://$host$request_uri;
}
}访问https://frpadmin.xlwx.top/输入用户名和密码即可访问 frp 管理页面了。
DNS 配置
- 添加 DNS 记录
登录域名管理平台,添加 A 记录:
| 主机记录 | 记录类型 | 记录值 | TTL |
|---|---|---|---|
| frp | A | 你的服务器 IP | 600 |
例如:frp.abc.com → A → 123.45.67.89
- 验证 DNS 生效
本地测试
$ ping frp.abc.com或使用 dig
$ dig frp.abc.com应该解析到你的服务器 IP
本地客户端配置
- 下载 frpc(客户端)
macOS:
创建目录
mkdir -p ~/frp
cd ~/frp下载
# macOS AMD64
wget https://github.com/fatedier/frp/releases/download/0.65.0/frp_0.56.0_darwin_amd64.tar.gz
# macOS ARM64
wget https://github.com/fatedier/frp/releases/download/0.65.0/frp_0.65.0_darwin_arm64.tar.gz解压
tar -xzf frp_0.56.0_darwin_amd64.tar.gz
cd frp_0.56.0_darwin_amd64Windows:
下载 Windows 版本https://github.com/fatedier/frp/releases/download/0.65.0/frp_0.56.0_windows_amd64.zip
解压到 C:\frp
Linux:
mkdir -p ~/frp
cd ~/frp
wget https://github.com/fatedier/frp/releases/download/0.65.0/frp_0.56.0_linux_amd64.tar.gz
tar -xzf frp_0.56.0_linux_amd64.tar.gz
cd frp_0.56.0_linux_amd64- 创建 frpc 配置文件
检查端口
$ lsof -i :7000$ cd frp
$ vim frpc.toml
$ cat frpc.toml
# frpc.toml - frp 客户端配置
# 服务端连接配置
serverAddr = "123.45.67.89" # ⚠️ 改为你的服务器 IP
serverPort = 7000
# 认证配置(必须与服务端一致)
auth.method = "token"
auth.token = "your_secure_token_here" # ⚠️ 必须与服务端一致
# 日志配置
log.to = "./frpc.log"
log.level = "info"
log.maxDays = 3
# Keycloak HTTPS 代理
[[proxies]]
name = "keycloak-https"
type = "https"
localIP = "127.0.0.1"
localPort = 8081
customDomains = ["frp.abc.com"] # ⚠️ 改为你的域名
# 如果需要同时支持 HTTP(不推荐)
# [[proxies]]
# name = "keycloak-http"
# type = "http"
# localIP = "127.0.0.1"
# localPort = 8081
# customDomains = ["frp.abc.com"]重要配置说明:
- serverAddr: 你的服务器 IP 地址
- auth.token: 必须与服务端的 token 完全一致
- customDomains: 你在 DNS 中配置的域名
- 启动 frpc 客户端
启动客户端
./frp_0.65.0_darwin_arm64/frpc -c frpc.toml
login to the server failed: dial tcp 123.45.67.89:7000: connect: operation timed out. With loginFailExit enabled, no additional retries will be attempted这个是服务器的安全组没有放开7000 端口,需要去服务器上配置安全组。
再次启动客户端,然后访问https://frpadmin.xlwx.top/static/#/proxies/https就能看到有https类型的隧道了。
$ ./frp_0.65.0_darwin_arm64/frpc -c frpc.toml或后台运行
nohup ./frp_0.65.0_darwin_arm64/frpc -c frpc.toml > /dev/null 2>&1 &- 配置 frpc 开机自启(macOS)
4.1 使用 launchd(推荐)
创建 plist 文件
$ cat > ~/Library/LaunchAgents/com.frpc.plist <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.frpc</string>
<key>ProgramArguments</key>
<array>
<string>$HOME/frp/frp_0.56.0_darwin_amd64/frpc</string>
<string>-c</string>
<string>$HOME/frp/frp_0.56.0_darwin_amd64/frpc.toml</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardErrorPath</key>
<string>$HOME/frp/frpc.err.log</string>
<key>StandardOutPath</key>
<string>$HOME/frp/frpc.out.log</string>
</dict>
</plist>加载服务
launchctl load ~/Library/LaunchAgents/com.frpc.plist启动服务
launchctl start com.frpc4.2 使用脚本
创建启动脚本 start-frpc.sh
#!/bin/bash
cd ~/frp/frp_0.56.0_darwin_amd64
./frp_0.65.0_darwin_arm64/frpc -c frpc.toml$ chmod +x ./start-frpc.sh4.3 使用 frpc.sh 管理脚本(启动/停止/状态/重启)
frpc操作脚本:
#!/bin/bash
###
# @Author: matiastang
# @Date: 2023-12-21 16:43:16
# @LastEditors: matiastang
# @LastEditTime: 2023-12-21 16:50:35
# @FilePath: /HulkPress/Users/matias/matias/MT/MTGithub/tool/frpc/frpc.sh
# @Description: MacOS 脚本启动停止frpc服务
###
FRPC_HOME="$HOME/matias/MT/MTGithub/tool/frpc"
client="$FRPC_HOME/frpc"
toml="$FRPC_HOME/frpc.toml"
log="$FRPC_HOME/log"
if [ "$1" = "start" ];then
nohup $client -c $toml > "$log/nohup_frpc.log" 2>&1 &
echo "frpc start success"
elif [ "$1" = "status" ];then
ps aux | grep frpc | grep -v grep | grep -v $1
echo "frpc status success"
elif [ "$1" = "reload" ];then
echo "frpc" `$client reload -c $toml`
elif [ "$1" = "restart" ];then
ps aux | grep frpc | grep -v grep | grep -v $1 | awk '{print $2}' | xargs kill -9
rm -rf "$log/frpc.log"
rm -rf "$log/nohup_frpc.log"
nohup $client -c $toml > "$log/nohup_frpc.log" 2>&1 &
echo "frpc restart success"
elif [ "$1" = "stop" ];then
ps aux | grep frpc | grep -v grep | grep -v $1 | awk '{print $2}' | xargs kill -9
rm -rf "$log/frpc.log"
rm -rf "$log/nohup_frpc.log"
echo "frpc stop success"
else
other_commands="$client $@"
$other_commands
fi切换到frpc.sh所在目录,修改文件权限后运行:
$ chmod 755 ./frpc.sh
$ ./frpc.sh start如果弹窗提示:无法打开"frpc",因为无法验证开发者。,这是被系统拦截了,在系统的安全与隐私的通用中,允许运行。
再次运行:
$ ./frpc.sh start
frpc start success添加软链到系统环境变量:
sudo ln -s $HOME/matias/MT/MTGithub/tool/frpc/frpc.sh /usr/local/bin/frpc
sudo ln -s /Users/matias/matias/MT/MTGithub/tool/frpc/frpc.sh /usr/local/bin/frpc
which frpc
/usr/local/bin/frpc# 启动frpc服务
$ frpc start
# 查看frpc启动状态
$ frpc status
# 重启frpc服务
$ frpc restart
# 停止frpc服务
$ frpc stopMacOS也可以使用brew services管理服务,比如frpc部署在MacOS上时,可以用其进行管理。
- 微信平台配置
现在我想在本地调试微信登录,微信登录成功后的回调使用 frp 代理到本地,这样我就可以在本地调试了。
- 登录 https://open.weixin.qq.com/
- 进入你的应用 → 开发信息
- 授权回调域 → 修改
- 填入:frp.abc.com(不含 https://)
- 保存
测试微信登录
- 打开浏览器(隐私模式)
- 访问:https://frp.abc.com/realms/vectorstory-test/account
- 点击"微信登录"
- 应该重定向到微信授权页面
- 微信扫码授权
- 回调到 Keycloak 完成登录
- 配置 frpc 客户端仪表盘
在frpc.toml中添加相关代码:
# 客户端仪表盘
webServer.addr = "127.0.0.1"
webServer.port = 7400
webServer.user = "admin"
webServer.password = "your_dashboard_password"访问http://127.0.0.1:7400并没有响应。
$ lsof -i :7400发现端口7400没有被占用,表明服务没启动成功,所以访问不了。
排查了一圈发现,原来是webServer相关配置写的位置不对。我放在了文件的最下面,中间有一些[[proxies]]。这样的话就没启动服务,需要把webServer提到顶部和serverAddr一级的位置!应该是.toml文件的不同位置解析出来的结果不一样,如果换成.json应该就不容易出错。
重新配置之后能正常访问http://127.0.0.1:7400了。本地服务启动成功后,日志文件中有个admin server listen on 127.0.0.1:7400的提示。
$ cat nohup_frpc.log
...
2023/12/22 16:18:03 [I] [service.go:177] admin server listen on 127.0.0.1:7400
...https2http 插件配置
如果本地只有http服务,又想通过https对外提供服务,可以在frpc.toml中使用https2http插件:
[[proxies]]
name = "web_http"
type = "http"
localPort = 3000
remotePort = 8080
customDomains = ["域名"]
[[proxies]]
name = "web_https"
type = "https"
remotePort = 8443
customDomains = ["域名"]
[proxies.plugin]
type = "https2http"
# 本地应用端口
localAddr = "127.0.0.1:3000"
# HTTPS 证书相关的配置,修改证书文件路径(证书要是customDomains中配置域名的证书)
crtPath = "/Users/matias/matias/matiasResources/certificates/***.pem"
keyPath = "/Users/matias/matias/matiasResources/certificates/***.key"
hostHeaderRewrite = "127.0.0.1"
requestHeaders.set.x-from-where = "frp"对应的服务端frps.toml开放了两个端口:8080用于监听http代理,8443用于监听https代理:
# 服务端监听端口
bindPort = 7000
# http代理监听端口
vhostHTTPPort = 8080
# https代理监听端口
vhostHTTPSPort = 8443完整架构图
如下是一个完整的本地调试微信登录的架构图
┌─────────────────────────────────────────────────────────────┐ │ 互联网 │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ 用户浏览器 │ │ 微信服务器 │ │ │ └──────┬───────┘ └──────┬───────┘ │ │ │ ①访问 │ ③回调 │ │ │ ④登录完成 │ │ │ ▼ ▼ │ │ ┌──────────────────────────────────────────┐ │ │ │ frp.abc.com (你的服务器) │ │ │ │ ┌──────────┐ ┌──────────┐ │ │ │ │ │ Nginx │ SSL │ frps │ │ │ │ │ │ :443 │───────▶│ :7000 │ │ │ │ │ └──────────┘ │ :80 │ │ │ │ │ └────┬─────┘ │ │ │ └───────────────────────────┼──────────────┘ │ └──────────────────────────────┼──────────────────────────────┘ │ frp 隧道 │ (加密传输) ┌──────────────────────────────┼──────────────────────────────┐ │ 本地开发环境 │ │ │ ▼ │ │ ┌────────────────────────────────┐ │ │ │ frpc │ │ │ │ (客户端) │ │ │ └────────────┬───────────────────┘ │ │ │ │ │ ▼ │ │ ┌────────────────────────────────┐ │ │ │ Keycloak (Docker) │ │ │ │ http://localhost:8081 │ │ │ │ ┌──────────────────────┐ │ │ │ │ │ WeChatAuthenticator │ │ │ │ │ └──────────────────────┘ │ │ │ └────────────────────────────────┘ │ └──────────────────────────────────────────────────────────────┘
常见问题
防病毒软件误删 frpc
一些防病毒软件错误地将 frpc 标记为恶意软件并将其删除。这是因为 frp 是一种能够创建反向代理的网络工具。防病毒软件有时会标记反向代理,因为它们能够绕过防火墙端口限制。如果您使用防病毒软件,则可能需要在防病毒设置中将 frpc 列入白名单/排除,以避免意外隔离/删除。有关更多详细信息,请参阅frp issues 3637。
customDomains 必须与绑定的域名一致
customDomains必须更新为实际绑定/解析的域名,必须要一样,写错了会导致无法访问。
使用 https 访问 frps 仪表盘
在frps.toml文件中添加证书配置:
# 服务端tls证书配置
# webServer.tls.certFile = "/usr/cert/[***].pem"
# webServer.tls.keyFile = "/usr/cert/[***].key"
webServer.tls.certFile = "server.crt"
webServer.tls.keyFile = "server.key"之后访问https://[serverAddr]:7500即可查看仪表盘。
toml 参数说明
# 必须包含 IPv6 的文字地址或主机名
# 在方括号中,如"[::1]:80"、"[ipv6-host]:http"或"[ipv6-host%zone]:80"
# 对于单个"bind_addr"字段,不需要方括号,如"bind_addr = ::"。
bindAddr = "0.0.0.0"
bindPort = 7000
# 用于 kcp 协议的 udp 端口,可以与 'bind_port' 相同。
# 如果未设置,kcp 在 frps 中被禁用。
kcpBindPort = 7000
# 用于 quic 协议的 udp 端口。
# 如果没有设置,quic 在 frps 中被禁用。
# quicBindPort = 7002
# 指定代理监听哪个地址,默认值与bind_addr相同
# proxy_bind_addr = "127.0.0.1"
# quic协议选项
# transport.quic.keepalivePeriod = 10
# transport.quic.maxIdleTimeout = 30
# transport.quic.maxIncomingStreams = 100000
# 心跳配置,不建议修改默认值
# heartbeat_timeout 默认值为 90。设置负值即可禁用。
# transport.heartbeatTimeout = 90
# 每个代理中的池计数将不超过 maxPoolCount。
transport.maxPoolCount = 5
# 如果使用tcp流复用,默认为true
# transport.tcpMux = true
# 指定 tcp mux 的保持活动间隔。
# 仅当 tcpMux 为 true 时才有效。
# transport.tcpMuxKeepaliveInterval = 60
# tcpKeepalive 指定 frpc 和 frps 之间活动网络连接的 keep-alive 探测之间的时间间隔。
# 如果为负,则禁用保持活动探测。
# transport.tcpKeepalive = 7200
# Transport.tls.force 指定是否只接受 TLS 加密的连接。 默认情况下,该值为 false。
tls.force = false
# transport.tls.certFile = "server.crt"
# transport.tls.keyFile = "server.key"
# transport.tls.trustedCaFile = "ca.crt"
# 如果要支持虚拟主机,必须设置监听的http端口(可选)
# 注意:http端口和https端口可以与bind_port相同
vhostHTTPPort = 80
vhostHTTPSPort = 443
# vhost http服务器的响应头超时(秒),默认为60秒
# vhostHTTPTimeout = 60
# tcpmuxHTTPConnectPort 指定服务器监听 TCP 的端口
# HTTP CONNECT 请求。 如果值为0,服务器不会复用TCP
# 单个端口上的请求。 如果不是 - 它将监听这个值
# HTTP CONNECT 请求。 默认情况下,该值为 0。
# tcpmuxHTTPConnectPort = 1337
# 如果 tcpmux_passthrough 为 true,frps 不会对流量进行任何更新。
# tcpmuxPassthrough = false
# 配置Web服务器以启用frps的仪表板。
# 仅当设置了 webServer.port 时,仪表板才可用。
webServer.addr = "127.0.0.1"
webServer.port = 7500
webServer.user = "admin"
webServer.password = "your_dashboard_password"
# webServer.tls.certFile = "server.crt"
# webServer.tls.keyFile = "server.key"
# dashboard assets directory(only for debug mode)
# webServer.assetsDir = "./static"
# 在仪表板监听器中启用 golang pprof 处理程序。
# 必须首先设置仪表板端口
# webServer.pprofEnable = false
#enablePrometheus 将在 /metrics api 中导出 webServer 上的 prometheus 指标。
# enablePrometheus = true
# 控制台或真实日志文件路径,如 ./frps.log
log.to = "./frps.log"
# trace, debug, info, warn, error
log.level = "info"
log.maxDays = 3
# 当 log.to 为控制台时禁用日志颜色,默认为 false
log.disablePrintColor = false
#DetailedErrorsToClient 定义是否将特定错误(带有调试信息)发送到 frpc。 默认情况下,该值为 true。
detailedErrorsToClient = true
# auth.method 指定使用什么身份验证方法对 frpc 和 frps 进行身份验证。
# 如果指定了"token" - 令牌将被读入登录消息。
# 如果指定"oidc" - 将使用 OIDC 设置颁发 OIDC(开放 ID 连接)令牌。 默认情况下,该值为"token"。
auth.method = "token"
# auth.additionalScopes 指定包含身份验证信息的附加范围。
# 可选值为 HeartBeats、NewWorkConns。
# auth.additionalScopes = ["HeartBeats", "NewWorkConns"]
# auth token
auth.token = "your_auth_token"
# oidc 发行者指定验证 OIDC 令牌的发行者。
# auth.oidc.issuer = ""
# oidcaudience 指定验证时 OIDC 令牌应包含的受众。
# auth.oidc.audience = ""
# oidc SkipExpiryCheck 指定是否在 OIDC 令牌过期时跳过检查。
# auth.oidc.skipExpiryCheck = false
# oidc skipIssuerCheck 指定是否跳过检查 OIDC 令牌的颁发者声明是否与 OidcIssuer 中指定的颁发者匹配。
# auth.oidc.skipIssuerCheck = false
# userConnTimeout 指定等待工作连接的最长时间。
# userConnTimeout = 10
# 只允许 frpc 绑定您列出的端口。 默认情况下,不会有任何限制。
# allowPorts = [
# { start = 2000, end = 3000 },
# { single = 3001 },
# { single = 3003 },
# { start = 4000, end = 50000 }
# ]
# 每个客户端可以使用的最大端口数,默认值为0表示无限制
maxPortsPerClient = 0
# 如果subDomainHost不为空,则可以在frpc的配置文件中设置type为http或https时的子域名
# subDomainHost = "frps.com"
# subdomain为test时,路由使用的host为test.frps.com
# subDomainHost = "*.proxy.frps.com"
# subdomain为test时,路由使用的host为test.proxy.frps.com
# HTTP 请求的自定义 404 页面
# custom404Page = "/path/to/404.html"
# 指定udp数据包大小,单位为字节。 如果未设置,则默认值为 1500。
# 这个参数在客户端和服务器之间应该是相同的。
# 它影响 udp 和 sudp 代理。
udpPacketSize = 1500
# NAT打洞策略数据的保留时间。
natholeAnalysisDataReserveHours = 168
# [[http插件]]
# name = "user-manager"
# addr = "127.0.0.1:9000"
# path = "/handler"
# ops = ["Login"]
# [[http插件]]
# name = "port-manager"
# addr = "127.0.0.1:9001"
# path = "/handler"
# ops = ["NewProxy"]参考
版权所有
版权归属:matiastang
许可证:Copyright © 2022-present tangdaoyong
