FRP 内网穿透与 Nginx 反向代理配置
9 个章节
使用 FRP 建立公网入口到内网服务的安全隧道,并通过 Nginx 统一处理 HTTPS 与反向代理。
1. 穿透原理
内网穿透的本质是流量中继。frp 通过在公网和内网之间建立一条长连接(隧道),将公网服务器的请求“搬运”到内网
流量接力路径:
- 客户端 (Browser):发起 HTTPS 请求
www.zc6.asia:443 - Nginx (入口层):识别路径(如
/dev-api/),通过127.0.0.1转发给本地 frps - frps (中转层):接收 Nginx 流量,通过隧道(Bind Port 8848)发往 frpc
- frpc (出口层):接收隧道流量,根据
localPort转发给内网真实服务 - 内网服务 (Service):处理请求并原路返回响应。
2. frp 核心组件配置详解
A. 服务端 (frps.toml)
运行在公网服务器。它主要负责监听和中转。
# frp 服务端监听端口,供 frpc 连接
bindPort = 8848
# 鉴权 Token(建议开启,防止他人滥用你的中转服务器)
auth.token = "your_secure_token"
# HTTP/HTTPS 的虚拟主机端口(如果不用 Nginx 转发,直接用域名访问则必填)
# 但本场景下由 Nginx 统一处理 SSL,所以此处可不配
B. 客户端 (frpc.toml)
运行在内网环境(如 192.168.0.37)。定义具体的转发规则。
serverAddr = "111.228.57.71"
serverPort = 8848
auth.token = "your_secure_token"
# 规则 1:运维端前端 (静态网页)
[[proxies]]
name = "op-web"
type = "tcp"
localIP = "127.0.0.1" # 如果 frpc 和 Web 都在同一台机器,用 127.0.0.1
localPort = 80
remotePort = 8880 # 对应公网服务器开启的端口
# 规则 2:后端 API (SpringBoot 等)
[[proxies]]
name = "backend-api"
type = "tcp"
localIP = "127.0.0.1"
localPort = 8081
remotePort = 18081
# 规则 3:图片/存储服务 (MinIO 等)
[[proxies]]
name = "image-storage"
type = "tcp"
localIP = "192.168.1.21" # 指向局域网内另一台机器的 IP
localPort = 8089
remotePort = 8089
3. Nginx 反向代理参数详解
在穿透场景下,Nginx 的配置决定了 Web 应用是否能感知到真实的请求上下文。
nginx配置解释
# 运维端后端接口转发
# --- 1. HTTP 自动跳转 HTTPS 模块 ---
server {
listen 80; # 监听 80 端口(普通 HTTP)
server_name www.zc6.asia; # 匹配你的域名
# 重定向逻辑:将所有 HTTP 请求永久重定向(301)到 HTTPS
location / {
return 301 https://$server_name$request_uri;
}
}
# --- 2. HTTPS 主配置模块 ---
server {
listen 443 ssl http2; # 开启 443 端口,启用 SSL 和 HTTP/2 协议
server_name www.zc6.asia;
# SSL 证书路径(由 Let's Encrypt 提供)
ssl_certificate /etc/letsencrypt/live/www.zc6.asia/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.zc6.asia/privkey.pem;
# --- 核心:内网穿透代理配置 ---
# 场景 A:后端 API 转发 (SpringBoot/Java)
location /dev-api/ {
# 转发目标:指向 frps 在本地开启的 18081 端口
# 结尾的 "/" 表示从转发路径中剥离 "/dev-api/"
proxy_pass http://127.0.0.1:18081/;
# 重点:Host 透传
# 作用:告诉后端真实的域名。如果后端有根据域名做逻辑校验(如 MinIO 签名),此项必填。
proxy_set_header Host $host;
# 重点:协议透传
# 作用:告诉后端原始请求是 https。防止后端在做重定向时将用户跳回 http。
proxy_set_header X-Forwarded-Proto $scheme;
# 重点:真实 IP 透传
# 作用:让后端程序通过 request.getRemoteAddr() 获取到真正的访客 IP,而非 127.0.0.1。
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# 场景 B:MinIO 图片服务 (带签名校验)
location /zkss/ {
# 结尾不加 "/":完整保留 "/zkss/" 路径发送给内网
# 例如:访问 /zkss/1.png -> 转发给内网 /zkss/1.png
proxy_pass http://127.0.0.1:8089;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
# 性能优化:针对大文件/图片,关闭缓冲,直接流式传输
proxy_buffering off;
# 增加缓冲区,防止 S3 签名链接超长导致请求头溢出
proxy_buffer_size 128k;
proxy_buffers 4 256k;
}
# --- 3. 静态资源与本地文件 ---
location /bds-webview {
alias /opt/webview/dist; # 映射服务器物理路径
try_files $uri $uri/ /index.html; # 单页面应用(SPA)路由支持
index index.html index.htm;
}
}
4. 核心参数对比表
| 参数 | 含义 | 穿透中的作用 |
|---|---|---|
localIP | 内网服务地址 | frpc 所在机器能访问到的服务 IP |
localPort | 内网服务端口 | 真实服务的监听端口(如 8080) |
remotePort | 公网映射端口 | 公网服务器暴露给 frps 使用的入口端口 |
proxy_pass | Nginx 转发路径 | 必须指向公网本地的 remotePort |
X-Forwarded-Proto | 原始协议字段 | 解决 HTTPS 穿透后变 HTTP 的协议识别问题 |
5. 故障排查逻辑梳理
- 隧道层 (frp):
- 检查
frpc日志是否显示login to server success - 检查
frpc日志是否显示start proxy [xxx] success - 在公网执行
netstat -tunlp | grep remotePort确认端口已开启
- 检查
- 网络层 (Firewall):
- 云服务器安全组必须开放
serverPort(8848) 和所有remotePort - 内网机器必须开放
localPort(如有 Windows 防火墙)
- 云服务器安全组必须开放
- 代理层 (Nginx):
- 检查
proxy_pass端口是否与remotePort严格对应 - 检查
nginx -t语法及是否有斜杠引起的路径偏移
- 检查
- 应用层:
- 在内网直接访问
localIP:localPort,确保服务本身正常
- 在内网直接访问
6.为什么 location 里面写“服务器公网 IP”也能访问?
在之前的尝试中,proxy_pass http://111.228.57.71:18081; 竟然也能通,这听起来很神奇,原因如下:
端口的“监听范围” (Listen on 0.0.0.0)
默认情况下,frps 开启的端口(如 18081)会监听在 0.0.0.0 上。这意味着这个端口不仅可以通过内部回环地址 127.0.0.1 访问,也可以通过服务器的“外网 IP”访问。
流量的“回环绕路”
当 Nginx 请求 111.228.57.71 时:
- Nginx 将请求发出,发向网卡。
- 操作系统的网络栈发现这个 IP 就是“我自己”。
- 流量直接在内核层被转回到了本地的
18081端口。 - 结果:请求成功了。
为什么不建议这么做? 虽然能通,但存在三大弊端:
- 安全性极低:如果你写公网 IP 能通,说明全世界任何人只要知道你的 IP 和端口,都能直接访问你的内网后端,跳过了 Nginx 的 SSL 加密和防火墙保护。
- 性能损耗:流量走了一圈网卡驱动,不如
127.0.0.1(回环地址)直接在内存中传递效率高。 - 防火墙依赖:如果你哪天为了安全,在云服务安全组里禁用了
18081的入站,写公网 IP 的方式就会立刻失效,而写127.0.0.1则永远不受影响。