本地自签名SSL证书

最终还是没有解决莫名其妙的重定向循环问题。隔个一两天就来一次,重启Apache就好了,不知道具体原因。但是大概率是https和http之间难舍难分的纠葛,所以选择了frps和frpc双SSL认证,用mkcert自签发证书。

让frps正确监听和转发HTTPS真是废了不少功夫,好累。

WordPress & frp 穿透架构循环重定向及 502 错误排查报告

报告日期: 2025年7月11日;记录者:gemini-2.5-pro

一、 初始问题与系统架构

  1. 初始问题: 外部通过域名 https://fanyiming.life 访问部署在本地 Windows XAMPP 环境下的 WordPress 站点时,出现无限循环重定向。
  2. 系统架构:
  • 客户端: 浏览器
  • 云服务器 (39.105.184.3): Nginx (处理 SSL/TLS) -> frps 服务
  • 内网穿透: frps <-> frpc
  • 本地服务器: frpc -> Windows XAMPP (Apache + PHP + WordPress)

二、 核心排查历程与关键发现

整个排查过程曲折但逻辑清晰,我们一步步剥离问题,最终定位到根源。

  1. 阶段一:怀疑 WordPress 协议判断错误
  • 现象: 无限重定向。
  • 分析: 这是典型的 WordPress 在反向代理后无法正确判断客户端协议 (HTTP/HTTPS) 导致的。虽然 Nginx 配置了 X-Forwarded-Proto 等头部,但问题依然存在,暗示更深层次的代理链问题。
  1. 阶段二:尝试建立端到端 HTTPS 隧道,遭遇 wrong version number
  • 目标: 为简化协议,我们决定让 Nginx 到 frps,再到 frpc,全程使用 HTTPS。
  • 操作: 配置 frps.ini 使用 vhost_https_port = 8080。
  • 关键发现 (1): curl https://127.0.0.1:8080 在 frps 服务器上返回 error:0A00010B:SSL routines::wrong version number。同时 curl http://… 能返回 frp 的页面。这铁证如山地证明了 frps 的 8080 端口当时仍在监听 HTTP 协议,而非 HTTPS。
  • 结论: frps 的 vhost_https_port 参数本身不会启用 TLS,必须手动配置证书。
  1. 阶段三:为 frps 配置证书,遭遇“幽灵”问题
  • 操作: 使用 mkcert 生成自签名证书,并在 frps.ini 中配置 vhost_https_cert_file 和 vhost_https_key_file。
  • 关键发现 (2): 即使配置了证书,并通过 systemctl restart frps.service 重启服务,wrong version number 错误依然存在。systemd 的日志显示“https service listen on …”,但 curl 的实际表现却与日志完全矛盾。
  • 结论: frps v0.51.3 在您的系统环境中存在一个严重的 Bug 或兼容性问题,导致其无法正确应用 TLS 设置到监听端口,尽管它声称已经这么做了。
  1. 阶段四:升级 frps,遭遇 unrecognized name
  • 操作: 将 frps 从 v0.51.3 大幅升级到 v0.63.0。
  • 关键发现 (3): 升级后,wrong version number 错误消失!取而代之的是新错误 error:0A000458:SSL routines::tlsv1 unrecognized name。这是一个巨大进步,证明新版 frps 已成功启用了 HTTPS。
  • 结论: 新错误是 SNI (Server Name Indication) 问题。frps 现在要求客户端在 TLS 握手时必须指明要访问的域名(fanyiming.life),而 curl 和 Nginx 直接访问 127.0.0.1,没有提供这个信息。
  1. 阶段五:解决 SNI 问题,大功告成
  • 操作:
  1. 修改 curl 测试命令,使用 –resolve 参数来模拟域名访问。
  2. 修改 Nginx 代理配置,添加 proxy_ssl_server_name on; 和 proxy_ssl_name fanyiming.life;,使其在代理时向 frps 表明身份。
  • 结果: Nginx 成功连接到 frps,502 Bad Gateway 错误消失,网站访问成功。

三、 根本原因分析

  1. 主要元凶: frps v0.51.3 的一个 Bug/兼容性问题,它无法在特定环境下正确启用 vhost_https_port 的 TLS 功能。
  2. 次要原因: Nginx 反向代理 SNI 配置缺失。在后端服务(新版 frps)要求 SNI 的情况下,Nginx 未配置相应的 proxy_ssl_server_name,导致连接被拒绝。
  3. 表面现象: WordPress 循环重定向和 Nginx 502 错误,均是由上述两个核心问题引发的连锁反应。

四、 最终解决方案

升级 frp: 将云服务器上的 frps 程序从 v0.51.3 升级到 v0.63.0。

frps.ini 配置
    [common]
    bind_port = 7000
    vhost_https_port = 8080
    token = ...
    vhost_https_cert_file = /path/to/selfsigned.pem
    vhost_https_key_file = /path/to/selfsigned.key
Nignx代理配置
    location / {
        proxy_pass https://127.0.0.1:8080;
        proxy_ssl_verify off;
        proxy_ssl_server_name on;            # 关键开启 SNI
        proxy_ssl_name fanyiming.life;      # 关键指定 SNI 名称

        # 其他头部设置
        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;
        # ...
    }

本地 frpc 配置: type 需设置为 https,local_port 指向本地 WordPress 服务的 HTTPS 端口。

五、 总结与建议

  • 当服务的日志输出与实际网络行为不符时,应高度怀疑软件本身的版本 Bug 或环境兼容性问题,果断升级是最高效的解决手段。
  • wrong version number 是协议不匹配的典型错误;unrecognized name 是 SNI 配置问题的典型错误。记住这两个特征能极大提高排查效率。
  • 在复杂的代理链中,确保每一环节的协议(HTTP/HTTPS)和主机名(SNI)都正确传递,是保证系统稳定运行的核心。

再次祝贺问题圆满解决!这次排查非常有价值。

配置思路:

  • 浏览器 → frps:HTTPS(公认CA证书)
  • frps → frpc:frp的HTTPS隧道(frp自带TLS加密)
  • frpc → 本地XAMPP:HTTPS(mkcert证书)
  • type = https 表示 frp 隧道本身用 TLS 加密,frps和frpc之间的流量不会被明文传输。
  • frps 监听 443 端口,使用公认CA证书,外部浏览器100%信任。
  • frps 收到 HTTPS 请求后,解密,再通过 frp 的 https 隧道加密转发到 frpc。
  • frpc 收到后,转发到本地 XAMPP 的 443 端口(mkcert证书)。
  • 全链路加密:浏览器→frps(HTTPS),frps→frpc(frp HTTPS隧道),frpc→XAMPP(HTTPS)。
  • 浏览器100%信任:外部用户无警告。
  • 本地开发体验好:本地mkcert证书,浏览器信任。
  • 安全性极高:即使公网、家庭宽带都不会有明文流量。
本地安装自签发证书
PS C:\xampp\mkcert (local_SSL)> .\mkcert-v1.4.4-windows-amd64.exe --install
Created a new local CA 💥
The local CA is now installed in the system trust store! ⚡️
Note: Firefox support is not available on your platform. 

PS C:\xampp\mkcert (local_SSL)> .\mkcert-v1.4.4-windows-amd64.exe fanyiming.life localhost 127.0.0.1 ::1

Created a new certificate valid for the following names 📜
 - "fanyiming.life"
 - "localhost"
 - "127.0.0.1"
 - "::1"

The certificate is at "./fanyiming.life+3.pem" and the key at "./fanyiming.life+3-key.pem"

It will expire on 11 October 2027 🗓

PS C:\xampp\mkcert (local_SSL)> copy fanyiming.life+3.pem C:\xampp\apache\conf\ssl.crt\fanyiming.life.crt
PS C:\xampp\mkcert (local_SSL)> copy fanyiming.life+3-key.pem C:\xampp\apache\conf\ssl.key\fanyiming.life.key
PS C:\xampp\mkcert (local_SSL)>

然后要记得在Apache配置虚拟主机。

C:\xampp\apache\conf\extra\httpd-ssl.conf
# 添加您的域名SSL虚拟主机
<VirtualHost fanyiming.life:443>
    DocumentRoot "C:/xampp/htdocs"
    ServerName fanyiming.life
    ServerAlias www.fanyiming.life
    
    # SSL配置
    SSLEngine on
    SSLCertificateFile "conf/ssl.crt/fanyiming.life.crt"
    SSLCertificateKeyFile "conf/ssl.key/fanyiming.life.key"
    
    # PHP支持
    <FilesMatch "\.(cgi|shtml|phtml|php)$">
        SSLOptions +StdEnvVars
    </FilesMatch>
    
    # 错误和访问日志
    ErrorLog "C:/xampp/apache/logs/fanyiming.life_error.log"
    CustomLog "C:/xampp/apache/logs/fanyiming.life_access.log" common
    
    # 设置安全头
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
    Header always set X-Frame-Options SAMEORIGIN
    Header always set X-Content-Type-Options nosniff
</VirtualHost>

打开 C:\xampp\apache\conf\httpd.conf,找到并取消注释以下两行:

httpd.conf
LoadModule ssl_module modules/mod_ssl.so
Include conf/extra/httpd-ssl.conf

云服务器上frps也需要一个mkcert证书(因为现在frps和frpc之间用https加密通讯)

shell
root@Eamon:/usr/local/frp/frp_0.63.0_linux_amd64# ll
total 19288
drwxr-xr-x 2 mysql smmta     4096 Jul 11 14:27 ./
drwxr-xr-x 4 root  root      4096 Jul 11 14:10 ../
-rwxr-xr-x 1 mysql smmta 19714200 Jun 25 12:10 frps*
-rw-r--r-- 1 root  root       232 Jul 11 14:10 frps.ini
-rw-r--r-- 1 mysql smmta    11358 Jun 25 12:14 LICENSE
-rw------- 1 root  root      1704 Jul 11 14:10 selfsigned.key
-rw-r--r-- 1 root  root      1444 Jul 11 14:10 selfsigned.pem
root@Eamon:/usr/local/frp/frp_0.63.0_linux_amd64# 

frps服务端配置文件:

frps.ini
root@Eamon:/usr/local/frp/frp_0.63.0_linux_amd64# cat frps.ini 

[common]
bind_port = 7000
vhost_https_port = 8080 # 注意必须是vhost_https_port而不是vhost_http_port
token = 4xxxxxxxxxxxxxxxm
vhost_https_cert_file = /usr/local/frp/frp_0.51.3_linux_amd64/selfsigned.pem # 证书必须加
vhost_https_key_file = /usr/local/frp/frp_0.51.3_linux_amd64/selfsigned.key

frpc.ini
[common]
server_addr = [ip地址]
server_port = 7000 (是云服务器上的7000端口用于与frps通讯)
token = 4xxxxxxxxxxxxxxxxxxxxxm

# 设置 SOCKS5 代理有时我想在博客上集成gemini需要梯子
# http_proxy = socks5://127.0.0.1:1080

log_file = ./frpc.log
log_level = info

# --- 每个代理是一个独立的节节名就是代理名 ---
[web]
type = https
local_port = 443 # 这个443是本地windows上面Apache监听的端口跟云服务器无关

custom_domains = fanyiming.life,www.fanyiming.life

xxxx_proxy.conf
#PROXY-START/

location ^~ /
{
    add_header X-Nginx-Scheme $scheme always;
    proxy_pass https://127.0.0.1:8080;  # 转发到frps服务器正在监听的8080端口(注意因为从443加密转发来的,必须用https开头不能是http)
    
    # --- 新增下面这两行因为frps只给fanyiming.life这个域名配置了证书不然无法通过SSL认证 ---
    proxy_ssl_server_name on;
    proxy_ssl_name fanyiming.life;

    proxy_ssl_verify off;  # 关键关闭证书校验因为我们是自签发证书这里不关闭也通不过认证
    
    
    proxy_set_header Host $host; # 传递客户端浏览器请求的名字
    proxy_set_header X-Forwarded-Host $host; # 传递客户端浏览器请求的标准主机名
    proxy_set_header X-Real-IP $remote_addr; # 传递客户端真实ip
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 传递客户端ip和代理的ip
    
    # --- 关键修改添加/确保正确的协议和端口头 ---
    proxy_set_header X-Forwarded-Proto $scheme; # 传递协议
    proxy_set_header X-Forwarded-Port $server_port; # 传递监听端口

    # proxy_set_header REMOTE-HOST $remote_addr; # 这个头通常不是必需的
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade"; # 修改为 "upgrade" 以支持 WebSocket
    proxy_http_version 1.1;
    proxy_redirect off; # wordpress让关闭重定向

}

#PROXY-END/
~            

评论

9 - 3 = ?
您的邮箱地址不会被公开。必填项已用 * 标注 如遇验证码无法通过,请勿使用无痕/隐私浏览器模式