在内网穿透的方案中,FRP (Fast Reverse Proxy) 无疑是目前的王者。然而,直接使用 FRP 的 HTTP 穿透往往面临两个问题:一是通过 IP:端口访问不够优雅,二是管理 HTTPS 证书比较麻烦。
本文将介绍一种生产环境级别的最佳实践:“Nginx 反向代理 + FRP”。
这种方案的核心思路是:让 Nginx 守在公网 VPS 的 80/443 端口处理域名和 SSL 加密,然后将解密后的流量转发给本地的 FRP 服务。这样既能利用 Nginx 强大的证书管理(配合 Certbot 自动续期),又能保证内网服务的安全性。
1. 架构原理
在开始之前,我们需要理解流量是如何流转的。很多新手配置失败,往往是因为没搞懂这一层转发关系。
 * 传统模式:用户 -> FRP Server (80端口) -> 内网机器
 * 本方案模式:用户 -> Nginx (443端口/SSL) -> 转发(本地环回) -> FRP Server (8090端口) -> 隧道 -> 内网机器
核心要点:公网 VPS 的 80 和 443 端口必须留给 Nginx,FRP 服务端必须避让,改用其他端口(如 8090)。
2. 准备工作
 * 公网 VPS 一台 (假设 IP 为 x.x.x.x)
 * 域名一个 (已解析到 VPS IP,例如 www.example.com)
 * 内网设备一台 (运行 Web 服务,如 NAS、博客等)
3. 服务端配置 (VPS)
我们将使用 FRP v0.52+ 推荐的 TOML 格式。
步骤 3.1:修改 frps.toml
找到 FRP 服务端的配置文件(通常位于 /etc/frp/frps.toml),重点修改 HTTP 监听端口。
# /etc/frp/frps.toml

bindPort = 7000        # FRP 客户端与服务端通信的端口

# 【关键配置】
# 不要写 80!80 要留给 Nginx。
# 这里我们指定 8090 作为 FRP 的 HTTP 接收端口。
vhostHTTPPort = 8090   

# 注意:这里不需要配置 vhostHTTPSPort,因为 HTTPS 在 Nginx 层就处理完了。

步骤 3.2:重启 frps
修改后,重启服务并确认端口状态。
sudo systemctl restart frps

4. 客户端配置 (内网机器)
在内网设备上配置 frpc.toml。
# frpc.toml

serverAddr = "x.x.x.x"  # 你的 VPS 公网 IP
serverPort = 7000       # 对应服务端的 bindPort

[[proxies]]
name = "my-web-service"
type = "http"
localIP = "127.0.0.1"
localPort = 80          # 你内网服务的真实端口 (如 80, 8080, 5000 等)
customDomains = ["www.example.com"] # 【必须】与 Nginx 配置的域名一致

启动客户端:
./frpc -c frpc.toml

5. Nginx 与 SSL 配置 (VPS)
这是最“魔法”的一步。我们将配置 Nginx 接管 80/443 端口,并申请 Let's Encrypt 免费证书。
步骤 5.1:安装 Nginx 与 Certbot
以 Ubuntu/Debian 为例:
sudo apt update
sudo apt install nginx certbot python3-certbot-nginx

步骤 5.2:确保 80 端口空闲
在申请证书前,确保没有其他程序(包括旧配置的 frps)占用 80 端口。
netstat -tunlp | grep 80
# 如果看到 frps 占用了 80,请回头看“步骤 3.1”,把 vhostHTTPPort 改掉并重启 frps。

步骤 5.3:申请 SSL 证书
运行 Certbot,它会自动帮你生成 SSL 证书并修改部分 Nginx 配置。
sudo certbot --nginx -d www.example.com

步骤 5.4:配置反向代理
Certbot 会自动生成 SSL 相关的配置,但我们需要手动添加“转发给 FRP”的逻辑。
编辑 Nginx 配置文件(通常在 /etc/nginx/sites-enabled/default 或 /etc/nginx/conf.d/你的域名.conf):
server {
    server_name www.example.com; # 你的域名

    # --- SSL 配置 (由 Certbot 自动生成,保持原样) ---
    listen 443 ssl; 
    ssl_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    # --- 【核心修改部分】 ---
    location / {
        # 1. 转发给本地的 FRP HTTP 端口 (步骤 3.1 中设置的端口)
        proxy_pass http://127.0.0.1:8090;

        # 2. 传递域名头 (FRP 靠这个区分不同的客户端)
        proxy_set_header Host $host;

        # 3. 传递真实 IP (方便内网服务记录日志)
        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 $scheme;
    }
}

# HTTP 强制跳转 HTTPS (Certbot 自动生成)
server {
    if ($host = www.example.com) {
        return 301 https://$host$request_uri;
    }
    listen 80;
    server_name www.example.com;
    return 404;
}

步骤 5.5:重载 Nginx
sudo nginx -t   # 检查语法
sudo systemctl reload nginx

6. 避坑指南 (Troubleshooting)
在部署过程中,最容易遇到以下三个报错,请对号入座:
1. Nginx 启动失败:Address already in use
 * 原因:FRP 服务端 (frps) 的 vhostHTTPPort 设置为了 80,抢了 Nginx 的端口。
 * 解决:修改 frps.toml,将 vhostHTTPPort 改为 8090,然后重启 frps,再启动 Nginx。
2. 访问网页报错:502 Bad Gateway
 * 原因:Nginx 就像前台,FRP 就像分机。502 意思是前台找不到分机了。通常是因为 Nginx 配置的 proxy_pass 端口(如 8080)和 frps 实际监听的端口(如 8090)不一致。
 * 解决:确保 Nginx 里的 proxy_pass http://127.0.0.1:XXXX 和 frps 里的 vhostHTTPPort = XXXX 是同一个数字。
3. 访问网页报错:404 Not Found (页面很简洁)
 * 原因:这是 FRP 返回的 404,说明 FRP 收到了请求,但找不到对应的内网客户端。
 * 解决:检查 frpc (内网客户端) 的 customDomains 是否拼写正确,必须和浏览器访问的域名完全一致。
7. 总结
通过这套方案,我们实现了:
 * 安全性:公网只暴露 Nginx 的 80/443,FRP 隐藏在后端。
 * 便利性:证书自动续期,不再需要手动分发证书到内网机器。
 * 扩展性:如果以后有新的内网服务(如 nas.example.com),只需在 FRP 客户端添加配置,并在 VPS Nginx 增加一个对应的 server 块即可。
希望这篇教程能帮你从端口冲突的泥潭中解脱出来,享受流畅的 HTTPS 内网穿透体验!