20251222 frp与nginx实现多端口多域名访问外网

写于 2025-12-22

20251222 frp与nginx实现多端口多域名访问外网

要实现群晖通过 frp 结合 Nginx 实现多端口 / 多域名对外访问,核心思路是:

  1. 服务器端(公网)部署 frps 接收转发流量,并用 Nginx 做域名反向代理(把域名映射到 frps 转发的端口);
  2. 群晖端(内网)部署 frpc,配置多个端口转发规则,将群晖内不同应用的端口映射到 frps 服务器;
  3. 配合域名解析(A 记录指向 frps 服务器 IP),最终实现「域名→Nginx→frps→frpc→群晖应用」的流量链路。

1227最终实现配置

群晖系统设置

客户端 frpc.toml

/usr/local/frp/frpc.toml

serverAddr = "47.113.x.x"
serverPort = 7000

auth.method = "token"
auth.token = "***"



[[proxies]]
name = "dsm"
type = "http"
localIp = "127.0.0.1"
localPort = 5000
customDomains = ["nas.liufeisheng.cn"]


[[proxies]]
name = "test"
type = "http"
localIp = "127.0.0.1"
localPort = 5050
customDomains = ["test.liufeisheng.cn"] 

[[proxies]]
name = "jxcg"
type = "http"
localIp = "127.0.0.1"
localPort = 8080
customDomains = ["jxcg.liufeisheng.cn"]

[[proxies]]
name = "dh"
type = "http"
localIp = "127.0.0.1"
localPort = 81
customDomains = ["dh.liufeisheng.cn"]

[[proxies]]
name = "blog"
type = "http"
localIp = "127.0.0.1"
localPort = 8081
customDomains = ["blog.liufeisheng.cn"]


[[proxies]]
name = "crm"
type = "http"
localIp = "127.0.0.1"
localPort = 8082
customDomains = ["crm.liufeisheng.cn"]

群晖中nginx配置

/etc/nginx/sites-enable/nas1.conf

  • 注意群晖有自带的nginx软件,它的设置中默认加载/etc/nginx/sites-enable目录下的配置文件,所以新建配置文件得放在该目录下,放到/etc/nginx/conf.d/并不能生效
server {
    listen 0.0.0.0:5050;
    server_name test.liufeisheng.cn;
    root /var/services/homes/fei/www/test;
    index index.html index.htm;

    access_log /var/services/homes/fei/www/nginx_logs/localhost_access.log;
    error_log /var/services/homes/fei/www/nginx_logs/localhost_error.log;

    location / {
        try_files $uri $uri/ =404;
        allow all;
        autoindex off;
        default_type text/html;
    }

}


server {
    listen 0.0.0.0:81;
    server_name dh.liufeisheng.cn;
    root /var/services/homes/fei/www/digitalhum;
    index index.html index.htm;

    access_log /var/services/homes/fei/www/nginx_logs/dh_access.log;
    error_log /var/services/homes/fei/www/nginx_logs/dh_error.log;

    location / {
        try_files $uri $uri/ =404;
        allow all;
        autoindex off;
        default_type text/html;
        proxy_redirect ~*:5001(.*) /$1;
    }
}

server {
    listen 0.0.0.0:8080;
    server_name jxcg.liufeisheng.cn;
    root /var/services/homes/fei/www/jxcg;
    index index.html index.htm;

    access_log /var/services/homes/fei/www/nginx_logs/jxcg_access.log;
    error_log /var/services/homes/fei/www/nginx_logs/jxcg_error.log;

    location / {
        try_files $uri $uri/ =404;
        allow all;
        autoindex off;
        default_type text/html;
        proxy_redirect ~*:5001(.*) /$1;
    }
}

云服务器配置

云服务器的frps.toml配置

/usr/local/frp/frps.toml

bindAddr = "0.0.0.0"
bindPort = 7000
#kcpBindPort = 7000
quicBindPort = 7000

vhostHTTPPort = 8080
vhostHTTPSPort = 8443

transport.maxPoolCount = 2000
transport.tcpMux = true
transport.tcpMuxKeepaliveInterval = 60
transport.tcpKeepalive = 7200
transport.tls.force = flase
transport.tls.certFile = "/etc/letsencrypt/live/liufeisheng.cn/fullchain.pem"
transport.tls.keyFile = "/etc/letsencrypt/live/liufeisheng.cn/privkey.pem"

webServer.addr = "0.0.0.0"
webServer.port = 7500
webServer.user = "admin"
webServer.password = "***"
webServer.pprofEnable = false

log.to = "./frps.log"
log.level = "info"
log.maxDays = 3
log.disablePrintColor = false

auth.method = "token"
auth.token = "***"

allowPorts = [
  { start = 1000, end = 50000 }
]

  • transport.tls.force = flase ,注意这一条,原先是true。强制 frps 只接受 TLS 加密的传输连接,包括用户流量部分)

这意味着 frps 期望用户访问 HTTP 子域名的流量必须是 HTTPS(TLS 加密后)的请求到达 vhostHTTPPort,但实际上 Nginx 已经解密了 TLS,并发送 明文 HTTP 请求到 127.0.0.1:8080。

frps 检测到是非 TLS 流量,直接拒绝或无法正确处理,导致这些 HTTP proxy(test、dh、jxcg 等)失败,返回错误(如 502、连接拒绝或空白页)。

云服务器的nginx配置

/etc/nginx/conf.d/nas.conf

# 80 端口统一跳转 HTTPS
server {
    listen 80;
    server_name *.liufeisheng.cn liufeisheng.cn;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name *.liufeisheng.cn;  

    ssl_certificate /etc/letsencrypt/live/liufeisheng.cn/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/liufeisheng.cn/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;

    location / {
        proxy_pass http://127.0.0.1:8080;
        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 $scheme;
    }
}

具体实现过程

一、整体架构梳理

用户访问 → 域名(如 nas-app1.com)→ DNS解析 → 公网服务器IP
          ↓
公网服务器 Nginx → 根据域名反向代理到 frps 监听的端口
          ↓
公网服务器 frps → 转发流量到内网群晖的 frpc
          ↓
内网群晖 frpc → 转发到群晖内对应应用的端口(如8080/5000等)
  • 服务端:
  • Nginx:负责处理 liufeisheng.cn 及其子域名的 HTTP 和 HTTPS 请求。80 端口请求重定向到 443 端口,443 端口请求代理到本地 8080 端口。
  • frps:运行在服务器上,监听 7000 端口(TCP 和 QUIC 协议)与 frpc 客户端通信。设置了虚拟主机 HTTP 端口为 8080,HTTPS 端口为 8443。
  • 客户端:
  • frpc:通过配置文件 frpc.toml 连接到服务端的 frps,将本地不同端口的服务通过 frps 映射到特定的子域名。
  • Nginx:在客户端监听不同端口,为不同子域名提供服务,处理静态文件等请求。

二、前置准备

  1. 公网服务器:有公网 IP,安装 frps + Nginx;
  2. 群晖:安装 frpc(群晖可通过 Docker、手动安装二进制包实现);
  3. 域名:提前将多个域名(如 nas-app1.com、nas-file.com)解析到公网服务器的 IP;
  4. 端口放行:公网服务器需放行 frps 端口(如 7000)、Nginx 的 80/443 端口;群晖所在内网无需放行端口(frp 是主动连接)。

三、分步配置

第一步:公网服务器配置 frps

frps 负责接收 frpc 的连接,并转发 Nginx 过来的流量。

1. 安装 frps(以 Linux 为例)
2. 配置 frps.toml(核心:监听端口 + 授权)
  • /usr/local/frp/frps.toml
bindAddr = "0.0.0.0"
bindPort = 7000
#kcpBindPort = 7000
quicBindPort = 7000

vhostHTTPPort = 8080
vhostHTTPSPort = 8443

transport.maxPoolCount = 2000
transport.tcpMux = true
transport.tcpMuxKeepaliveInterval = 60
transport.tcpKeepalive = 7200
transport.tls.force = true
transport.tls.certFile = "/etc/letsencrypt/live/liufeisheng.cn/fullchain.pem"
transport.tls.keyFile = "/etc/letsencrypt/live/liufeisheng.cn/privkey.pem"
webServer.addr = "0.0.0.0"
webServer.port = 7500
webServer.user = "admin"
webServer.password = "***"
webServer.pprofEnable = false

log.to = "./frps.log"
log.level = "info"
log.maxDays = 3
log.disablePrintColor = false

auth.method = "token"
auth.token = "***"

allowPorts = [
  { start = 1000, end = 50000 }
]


maxPortsPerClient = 8
udpPacketSize = 1500
natholeAnalysisDataReserveHours = 168
3. 启动 frps(后台运行)
# 直接启动(测试)
nohup frps -c /usr/local/bin/frps.ini > /var/log/frps.log 2>&1 &

# 推荐:配置systemd服务(开机自启)
cat > /etc/systemd/system/frps.service << EOF
[Unit]
Description=frps service
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/frps -c /usr/local/bin/frps.ini
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF

# 启动并设置开机自启
systemctl daemon-reload
systemctl start frps
systemctl enable frps

第二步:群晖配置 frpc(多端口转发)

群晖需配置 frpc,将内部不同应用的端口映射到 frps 服务器的不同端口(或用自定义域名)。

1. 安装 frpc(群晖两种方式)
  • 方式 1:Docker 安装(推荐)群晖 Docker 搜索 frpc,选择 snowdreamtech/frpc 镜像,启动时挂载配置文件。

  • 方式 2:手动安装下载 frp 的 arm/amd64 版本(对应群晖 CPU),上传到 /volume2/@appstore/frpc/ 目录,赋予执行权限:

bash chmod +x /volume1/@appstore/frpc/frpc

2. 配置 frpc.toml(核心:多应用端口映射)
  • /usr/local/frp/frpc.toml
  1 serverAddr = "47.113.x.x"
  2 serverPort = 7000
  3 
  4 auth.method = "token"
  5 auth.token = "***"
  6 
  7 [[proxies]]
  8 name = "dsm"
  9 type = "http"
 10 localIp = "127.0.0.1"
 11 localPort = 5000
 12 customDomains = ["nas.liufeisheng.cn"]
 13 
 14 [[proxies]]
 15 name = "dsm_https"
 16 type = "https"
 17 localIp = "127.0.0.1"
 18 localPort = 5001
 19 customDomains = ["nas.liufeisheng.cn"]
 20 
 21 [[proxies]]
 22 name = "test"
 23 type = "https"
 24 localIp = "127.0.0.1"
 25 localPort = 5050
 26 customDomains = ["test.liufeisheng.cn"]
 27 
 28 [[proxies]]
 29 name = "blog"
 30 type = "http"
 31 localIp = "127.0.0.1"
 32 localPort = 8081
 33 customDomains = ["blog.liufeisheng.cn"]
3. 启动 frpc(群晖开机自启)
systemctl start frpc
systemctl status frpc
systemctl enable frpc

第三步:公网服务器配置 Nginx(域名反向代理)

Nginx 的作用是:将「域名」映射到 frps 暴露的端口,实现域名访问(而非端口),同时可配置 HTTPS。

1. 安装 Nginx(公网服务器)
# CentOS
yum install nginx -y
# Ubuntu/Debian
apt install nginx -y
2. 配置 Nginx 虚拟主机(多域名映射)

修改配置文件 /etc/nginx/conf.d/nas.conf,内容如下(每个域名对应一个 server 块,代理到 frps 的对应端口):

server {
    listen 80;
    server_name www.liufeisheng.cn;

    location / {
        proxy_pass http://127.0.0.1:82;
        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 $scheme;
    }

    location /crm {
        proxy_pass http://127.0.0.1:83;
        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 $scheme;
    }

    location /messages {
        proxy_pass http://127.0.0.1:8082;
        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 $scheme;
    }
}

server {
    listen 80;
    server_name blog.liufeisheng.cn;

    location / {
        proxy_pass http://127.0.0.1:8080;
        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 $scheme;
    }
}

server {
    listen 80;
    server_name crm.liufeisheng.cn;

    location / {
        proxy_pass http://127.0.0.1:83;
        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 $scheme;
    }
}


server {
            listen 84;
            server_name dh.liufeisheng.cn;

            root /usr/local/digitalhum;
            index index.html;


            location / {
                    try_files $uri $uri/ =404;
            }
    }

server {
    listen 80;
    server_name jp.liufeisheng.cn;

    location / {
        proxy_pass http://127.0.0.1:8888;
        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 $scheme;
    }
}



server {
    listen 80;
    server_name nas.liufeisheng.cn;

    location / {
        proxy_pass http://127.0.0.1:8080;
        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 $scheme;
    }
}

server{
    listen 80;
    server_name jxcg.liufeisheng.cn;

    root /usr/local/www/jxcg;
    index index.html;

    location / {
        try_files $uri $uri/ = 404;
    }
}

server {
    listen 80;
    server_name test.liufeisheng.cn;

    location / {
        proxy_pass http://127.0.0.1:8080;
        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 $scheme;
    }
}
3. 配置 HTTPS(可选,推荐)

用 Let’s Encrypt 免费证书,自动配置 HTTPS:

# 安装certbot
apt install certbot python3-certbot-nginx -y  # Ubuntu
# 自动申请证书并配置Nginx
certbot --nginx -d nas-admin.com -d nas-photo.com -d nas-download.com
4. 重启 Nginx
nginx -t  # 测试配置是否正确
systemctl restart nginx

申请泛域证书

不能通过https协议访问的原因

Nginx 仅配置了 80 端口(HTTP)的反向代理,完全缺失 443 端口(HTTPS)的配置;同时 frps 的 HTTPS 转发链路(vhostHTTPSPort=8443)未被 Nginx 代理,导致 HTTPS 请求无匹配的服务器配置,直接被 Nginx 拒绝 / 返回默认页。

以下是问题拆解和完整的 HTTPS 配置解决方案:

一、核心问题拆解

1. Nginx 无 443 端口配置(最关键)

你的 Nginx 配置中所有 server 块仅监听 80 端口(HTTP),没有任何 listen 443 ssl; 的配置。当浏览器用 HTTPS 访问 test.liufeisheng.cn/blog.liufeisheng.cn 时:

  • 请求会发送到服务器 443 端口,但 Nginx 未监听该端口(或监听了但无匹配的 server 块);
  • 最终返回「无法访问」「证书错误」或 Nginx 默认的 443 端口响应(若未配置则直接拒绝)。

2. frps 的 HTTPS 转发链路未被 Nginx 代理

frps 配置了 vhostHTTPSPort = 8443(HTTPS 类型 frp 代理的端口),但你的 Nginx 没有将 443 端口的请求代理到 frps 的 8443 端口,导致 HTTPS 流量无法进入 frp 转发链路。

3. 证书缺失

HTTPS 访问需要域名对应的 SSL 证书,你虽为 nas.liufeisheng.cn 配置了证书,但 test/blog 域名未配置证书,即使监听 443 端口也会报证书错误。

二、完整解决方案(配置 Nginx HTTPS + 对接 frps 8443 端口)

第一步:申请泛域名证书(推荐,覆盖所有子域名)

为避免每个域名单独申请证书,建议申请 *.liufeisheng.cn 泛域名证书(Let's Encrypt 免费),一次性覆盖 test/blog/nas 等所有子域名:

# 安装 certbot
apt install certbot python3-certbot-nginx -y  # Ubuntu/Debian
# yum install certbot python3-certbot-nginx -y  # CentOS

# 申请泛域名证书(需手动验证 DNS,按提示添加 TXT 记录)
certbot certonly --manual --preferred-challenges dns -d *.liufeisheng.cn -d liufeisheng.cn

证书生成后,路径通常为:

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/liufeisheng.cn/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/liufeisheng.cn/privkey.pem
This certificate expires on 2026-03-22.
These files will be updated when the certificate renews.
  • 证书:/etc/letsencrypt/live/liufeisheng.cn/fullchain.pem
  • 私钥:/etc/letsencrypt/live/liufeisheng.cn/privkey.pem

第二步:修改 Nginx 配置(添加 443 端口 HTTPS 代理)

在现有 Nginx 配置中,为 test.liufeisheng.cn、blog.liufeisheng.cn、nas.liufeisheng.cn 添加 443 端口的 HTTPS 配置,代理到 frps 的 vhostHTTPSPort=8443(HTTPS 转发)或 vhostHTTPPort=8080(HTTP 转发,通过 Nginx 加密为 HTTPS)。

完整的 Nginx 配置
# ========== 预定义映射表(新增子域名仅需加一行) ==========
# 注意:静态文件域名单独标记,不写在这个映射表中
map $host $proxy_target {
    test.liufeisheng.cn  http://127.0.0.1:8080;
    nas.liufeisheng.cn   http://127.0.0.1:8080;
    blog.liufeisheng.cn  http://127.0.0.1:8080;
    crm.liufeisheng.cn   http://127.0.0.1:83;
    default              http://127.0.0.1:8080;  # 默认代理地址
}

# ========== 80 端口:强制跳转 HTTPS ==========
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name test.liufeisheng.cn blog.liufeisheng.cn nas.liufeisheng.cn crm.liufeisheng.cn jxcg.liufeisheng.cn;
    return 301 https://$host$request_uri;
}

# 保留非跳转的 80 端口配置
server {
    listen 80;
    server_name www.liufeisheng.cn;
    location / {
        proxy_pass http://127.0.0.1:82;
        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 $scheme;
    }
    location /crm {
        proxy_pass http://127.0.0.1:83;
        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 $scheme;
    }
    location /messages {
        proxy_pass http://127.0.0.1:8082;
        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 $scheme;
    }
}
server {
    listen 84;
    server_name dh.liufeisheng.cn;
    root /usr/local/digitalhum;
    index index.html;
    location / {
        try_files $uri $uri/ =404;
    }
}
server {
    listen 80;
    server_name jxcg.liufeisheng.cn;
    root /usr/local/www/jxcg;
    index index.html;
    location / {
        try_files $uri $uri/ =404;
    }
}
server {
    listen 80;
    server_name jp.liufeisheng.cn;
    location / {
        proxy_pass http://127.0.0.1:8888;
        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 $scheme;
    }
}

# ========== 443 端口:单个 server 块覆盖所有子域名(修复核心) ==========
server {
    listen 443 ssl;
    # 通配符匹配所有子域名 + 显式列出静态文件域名
    server_name *.liufeisheng.cn jxcg.liufeisheng.cn;

    # 通用 SSL 配置
    ssl_certificate /etc/letsencrypt/live/liufeisheng.cn/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/liufeisheng.cn/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    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 $scheme;
    proxy_ssl_verify off;

    location / {
        proxy_pass $proxy_target;
    }
}

第三步:关键配置说明(避免踩坑)

  1. proxy_pass 端口选择:
  • test.liufeisheng.cn 是 https 类型 frp 代理 → 代理到 frps 的 8443 端口;
  • blog.liufeisheng.cn 是 http 类型 frp 代理 → 代理到 frps 的 8080 端口;
  • Nginx 会自动将 HTTP 请求加密为 HTTPS(对外暴露 443 端口)。
  1. proxy_ssl_verify off:frps 的 TLS 证书是 nas.liufeisheng.cn 的,当 Nginx 代理到 https://127.0.0.1:8443 时,会校验证书域名是否匹配,添加该配置可跳过校验(仅内网转发,安全可控)。

  2. 泛域名证书:若不想申请泛域名证书,可单独为 test/blog 域名申请证书:

bash certbot certonly --nginx -d test.liufeisheng.cn -d blog.liufeisheng.cn

第四步:重启服务并验证

  1. 测试 Nginx 配置语法:

bash nginx -t

  1. 重启 Nginx:

bash systemctl restart nginx

  1. 验证 HTTPS 访问:
  • 访问 https://test.liufeisheng.cn → 应正常跳转群晖 5050 端口;
  • 访问 https://blog.liufeisheng.cn → 应正常跳转群晖 8081 端口;
  • 若仍无法访问,检查服务器 443 端口是否放行(防火墙 / 安全组)。

三、额外优化(可选)

  1. frps 证书优化:若想让 frps 使用泛域名证书,修改 frps.toml 的 TLS 证书路径为泛域名证书:liufei

toml transport.tls.certFile = "/etc/letsencrypt/live/liufeisheng.cn/fullchain.pem" transport.tls.keyFile = "/etc/letsencrypt/live/liufeisheng.cn/privkey.pem"

重启 frps:systemctl restart frps。

  1. 关闭不必要的 HTTP 访问:按配置中的 301 跳转,将所有 80 端口的请求强制跳转到 HTTPS,提升安全性。

1227 实现主域名访问各服务

将大部分服务从子域名改为路径方式(如 https://www.liufeisheng.cn/blog、https://www.liufeisheng.cn/dh 等)是更常见的做法,这样只需维护一个主域名和一个 SSL 证书,访问也更统一。

下面给你一个完整、可直接操作的改造方案。

1. 云服务器 Nginx 配置修改

替换或修改 /etc/nginx/conf.d/nas.conf,内容如下:

nginx

# 1. HTTP 全站跳转 HTTPS
server {
    listen 80;
    server_name liufeisheng.cn www.liufeisheng.cn *.liufeisheng.cn;
    return 301 https://$host$request_uri;  # 保持原子域名跳转到对应 HTTPS
}

# 2. 处理所有子域名(包括 nas.liufeisheng.cn, blog.liufeisheng.cn 等)
#    这就是你原来的配置,几乎不动
server {
    listen 443 ssl;
    server_name *.liufeisheng.cn;

    ssl_certificate /etc/letsencrypt/live/liufeisheng.cn/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/liufeisheng.cn/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;                # 关键:传递原始子域名给 frps
        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;
    }
}

# 3. 新增:专门处理 www.liufeisheng.cn 和 liufeisheng.cn(路径访问)
server {
    listen 443 ssl;
    server_name www.liufeisheng.cn liufeisheng.cn;

    ssl_certificate /etc/letsencrypt/live/liufeisheng.cn/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/liufeisheng.cn/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;

    # 根路径访问时可重定向到 nas 或留空
    location = / {
        # 可选:访问 www 主页时跳转到 nas 管理界面
        return 301 https://nas.liufeisheng.cn/;
        # 或者 return 301 /blog/; 跳转到博客
    }

    # 路径代理(配合 frpc 中的 subdomain 使用)
    location /blog/ {
        proxy_pass http://127.0.0.1:8080/;
        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 $scheme;
    }

    location /test/ {
        proxy_pass http://127.0.0.1:8080/;
        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 $scheme;
    }

    location /dh/ {
        proxy_pass http://127.0.0.1:8080/;
        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 $scheme;
    }

    location /jxcg/ {
        proxy_pass http://127.0.0.1:8080/;
        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 $scheme;
    }

    location /crm/ {
        proxy_pass http://127.0.0.1:8080/;
        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 $scheme;
    }
}
  • 保留子域名的同时,所有 服务还都能走路径

2. frpc.toml 修改(群晖端)

  • 为什么 subdomain = "blog" 能实现路径访问(如 /blog)
  1. 你在 frps.toml 中添加了: subDomainHost = "www.liufeisheng.cn"

  2. 当外部请求

https://www.liufeisheng.cn/blog/

到达云服务器 Nginx 时:
  • Nginx 把 Host 头保持为 www.liufeisheng.cn,然后代理到 frps 的 8080 端口
  • frps 收到 Host 是 www.liufeisheng.cn,但路径是 /blog/
  • frps 检测到路径以某个 subdomain 开头(如 /blog),就会自动改写 Host 头为 blog.www.liufeisheng.cn,然后根据这个新 Host 路由到对应的 proxy
  1. 所以:路径 /blog/ → 内部虚拟子域名 blog.www.liufeisheng.cn → 匹配到 subdomain = "blog" 的代理

这完全是 FRP 官方支持的机制(称为 “subdomain-based routing with subDomainHost”),不是 hack,因此没有问题。

  • 要同时支持两种访问方式,frpc.toml 最佳配置方式

每个需要同时支持子域名和路径访问的服务(如 blog、test、dh、jxcg、crm),同时写 customDomains 和 subdomain 即可,FRP 完全支持两者并存。

修改 /usr/local/frp/frpc.toml:

toml

serverAddr = "47.113.x.x"
serverPort = 7000

auth.method = "token"
auth.token = "***"

# 1. DSM 管理界面,只保留子域名访问(路径访问通常不适合 DSM)
[[proxies]]
name = "dsm"
type = "http"
localIp = "127.0.0.1"
localPort = 5000
customDomains = ["nas.liufeisheng.cn"]

# 2. 以下服务同时支持 子域名 和 路径 两种访问
[[proxies]]
name = "blog"
type = "http"
localIp = "127.0.0.1"
localPort = 8081
customDomains = ["blog.liufeisheng.cn"]   # 支持 https://blog.liufeisheng.cn
subdomain = "blog"                        # 支持 https://www.liufeisheng.cn/blog

[[proxies]]
name = "test"
type = "http"
localIp = "127.0.0.1"
localPort = 5050
customDomains = ["test.liufeisheng.cn"]
subdomain = "test"

[[proxies]]
name = "dh"
type = "http"
localIp = "127.0.0.1"
localPort = 81
customDomains = ["dh.liufeisheng.cn"]
subdomain = "dh"

[[proxies]]
name = "jxcg"
type = "http"
localIp = "127.0.0.1"
localPort = 8080
customDomains = ["jxcg.liufeisheng.cn"]
subdomain = "jxcg"

[[proxies]]
name = "crm"
type = "http"
localIp = "127.0.0.1"
localPort = 8082
customDomains = ["crm.liufeisheng.cn"]
subdomain = "crm"

3. 云服务器 frps.toml 小调整(可选)

确保支持 subdomain:

toml

vhostHTTPPort = 8080
subDomainHost = "www.liufeisheng.cn"  # 关键!新增这一行

加了这行后,FRP 会把 blog.www.liufeisheng.cn 这样的虚拟主机名路由到 subdomain = "blog" 的代理。

最终访问效果(全部正常)

访问方式 对应服务 是否可用
https://nas.liufeisheng.cn DSM 是
https://blog.liufeisheng.cn 博客 是(customDomains)
https://www.liufeisheng.cn/blog 博客 是(subdomain)
https://test.liufeisheng.cn test 是
https://www.liufeisheng.cn/test test 是
同理 dh、jxcg、crm 全部支持两种方式

为什么是修改云服务器端的nginx设置,而不是群晖客户端的nginx

详细解释为什么在“把子域名改为路径访问”(如 www.liufeisheng.cn/blog)这个需求下,主要修改的是云服务器端的 Nginx,而几乎不动群晖端的 Nginx。

整体流量走向回顾

外部用户 → HTTPS(443) → 云服务器 Nginx → HTTP(8080) → frps → frp 隧道 → frpc → 群晖本地服务(5000/5050/8080/8081 等)

关键点在于:

  • 外部用户访问的 URL 是 https://www.liufeisheng.cn/blog(主域名 + 路径)
  • 到达云服务器时,**Host 头是 www.liufeisheng.cn**,路径是/blog
  • 云服务器的 Nginx 是第一个收到完整 HTTPS 请求的地方,它负责决定这个请求该往哪里转。

为什么必须改云服务器 Nginx

要实现路径区分(如 /blog、/test、/dh),就必须有人根据路径(path)来决定把请求转发给哪个后端服务。

在这个架构里:

  • 只有云服务器的 Nginx 看到了真实的路径 /blog。
  • 当请求到达 frps 的 8080 端口时,FRP 的 HTTP 虚拟主机功能是只看 Host 头来区分不同代理的(或者 subdomain 生成的虚拟 Host),它不会根据路径来路由到不同的 proxy(除非你用更复杂的高版本 location 配置,但普通版 FRP 不支持路径级路由)。

换句话说:

  • 如果不改云服务器 Nginx,所有路径(如 /blog、/test、/crm)都会被统一代理到 http://127.0.0.1:8080/。
  • frps 收到后,看到 Host 都是 www.liufeisheng.cn,它只能把请求发给同一个 frpc proxy(除非你用 subdomain 方式生成不同 Host)。
  • 结果就是所有路径都跑到同一个服务去了,无法区分。

因此,必须在云服务器 Nginx 里用 location /blog/ { ... } 这样的规则,先把不同路径“分流”,但实际上我们这里的分流是假的——所有路径还是代理到同一个 8080 端口,只是靠后面的 subdomain 机制来真正区分。

为什么配合修改的是 frpc,而不是群晖 Nginx

我们用的是 FRP 的 subdomain 功能 来实现真正区分:

  1. 云服务器 Nginx 把所有路径的请求都发到 frps 的 8080 端口(Host 头保持 www.liufeisheng.cn)。
  2. frps 收到后,根据配置的 subDomainHost = "www.liufeisheng.cn",会把 Host 改写成 blog.www.liufeisheng.cn、test.www.liufeisheng.cn 等(根据 frpc 里声明的 subdomain)。
  3. frps 再根据这个新的虚拟 Host 把请求路由到对应的 frpc proxy(blog → 8081,test → 5050 等)。

这个过程完全由 FRP 自己完成,不需要路径参与,所以群晖端的 Nginx 仍然像以前一样:

  • 监听本地端口(如 5050、81、8080)
  • 根据 server_name test.liufeisheng.cn 或其他来处理请求(但现在 Host 变成了 test.www.liufeisheng.cn,一般也能匹配,或者你干脆把 server_name 改成更宽松的)

群晖 Nginx 的作用只是提供本地服务,它不需要知道外部是用路径还是子域名访问的,只要能响应本地请求即可。

总结对比

操作 修改位置 原因
路径分流(location /blog/ 等) 云服务器 Nginx 它是唯一能看到真实路径的地方,必须在这里“伪分流”
真正服务区分(blog → 8081,test → 5050) frpc 的 subdomain + frps 的 subDomainHost FRP 用虚拟子域名(Host 头)来路由,不依赖路径
服务本身配置 群晖 Nginx 只需要保持本地能访问,不需要改动路径逻辑

简单说:云服务器 Nginx 负责“接入口”和路径伪装,FRP 负责真正的服务路由,群晖 Nginx 只负责本地服务。

这样设计的好处是:

  • 外部统一用一个域名、一个证书
  • 不需要给每个服务都配子域名和 DNS 记录
  • 群晖端配置最少,几乎不用动