一次因企微域名限制引发的“骚操作”:Nginx 与 FRP 的踩坑排查实录

嘿,今天,跟大家分享一个前几天遇到的真实案例,那叫一个曲折离奇,集"奇葩需求"、"骚操作"、"连环坑"于一体,堪称一次经典的"从入门到放弃再到超神"的网络排查之旅。

故事的起因,源于一个我们正在对接的项目,需要深度集成企业微信

缘起:那个"恼人"的固定域名

做过企微开发的朋友可能都懂,在配置应用时,它老人家有一个非常"贴心"的要求:你必须提供一个固定的、可信的源域名。在我们的项目中,客户早就配置好了 https://xc-test.hlj.com 这个域名。

然而,我们内部的开发测试环境,真实的访问地址是 https://xc.f.wmeimob.com。这个域名通过内网穿透工具 FRP 指向我们开发同学的本地环境。

一边是"御赐"的尚方宝剑(固定域名),一边是我们的"革命根据地"(开发域名)。怎么办?总不能让开发同学改个代码发布一下服务吧?

这时候,一个"天才"的想法诞生了:"简单!让客户把 xc-test.hlj.com CNAME 到我们的 xc.f.wmeimob.com 不就行了嘛!"

听起来天衣无缝,对吧?我当时也是这么想的。然而,我还是太年轻了...

第一回合:神秘的 301 重定向 👻

配置完 CNAME,我们满怀期待地打开 https://xc-test.hlj.com,结果浏览器地址栏"唰"地一下,跳转到了 https://xc-test.hlj.com/zentao

zentao 是我们内部的另一个项目管理工具,跟这个项目八竿子打不着。这感觉就像你点了份宫保鸡丁,外卖小哥却给你送来一份意大利面,还问你:"帅哥,你的面到了!" 离谱!🤯

链路分析: 我们的网络架构是这样的:

graph TD; A["用户浏览器"] -->|"1.访问 xc-test.hlj.com"| B("DNS服务器"); B -->|"2. CNAME解析"| C("xc.f.wmeimob.com"); C -->|"3. A记录解析"| D["公网Nginx服务器IP"]; A -->|"4. 发起HTTPS请求
Host: xc-test.hlj.com"| D; D -->|"5. Nginx处理"| E("FRP服务端(frps)"); E -->|"6. 转发"| F("FRP客户端(frpc)"); F -->|"7. 访问"| G["内网开发环境"];

问题出在哪?第一反应是 FRP 转发出错了。但冷静下来一想,FRP 不太可能主动发起一个到 /zentao 的重定向。最大的嫌疑人,只能是流量入口------公网 Nginx 服务器

登上服务器,grep -r "zentao" /etc/nginx/conf.d/ 走起!果然,在 chandao.conf 文件里,我们找到了罪魁祸首:

nginx 复制代码
# File: chandao.conf
server {
	listen 443 ssl;
	server_name zd.f.wmeimob.com;
	if ($request_uri = '/') {
		return 301 https://$host/zentao; # 啊哈!就是你!
	}
    ...
}

可问题是,我们访问的域名是 xc-test.hlj.com,为什么会匹配到 server_namezd.f.wmeimob.com 的配置呢?

这里就涉及到一个 Nginx 的核心知识点:默认服务器(Default Server) 。当 Nginx 收到一个请求,但找不到任何一个 server_name 能精确匹配请求的 Host 头时,它会把请求交给该端口的"默认服务器"处理。如果你没有用 listen 443 default_server; 显式指定,那么 Nginx 就会把配置文件中第一个 监听该端口的 server 块作为默认服务器。

不幸的是,chandao.conf 正是那个"天选之子"。它成为了 443 端口的默认处理程序,把我们无辜的请求,无情地重定向了。

解决方案 :修改 frps.conf,让 Nginx 明确知道谁才是这个域名的"主人"。

nginx 复制代码
# File: frps.conf
server {
    listen 443 ssl;
    # 把新域名加进去
    server_name *.f.wmeimob.com xc-test.hlj.com;
    ...
}

nginx -s reload,刷新页面,以为大功告成!结果...

第二回合:FRP 的无情拒绝 🙅‍♂️

页面不再跳转了,但取而代之的是一张冰冷的"not found"脸:

The page you visit not found. Sorry, the page you are looking for is currently unavailable. Please try again later. The server is powered by frp.

这个页面是 FRP 服务端(frps)返回的。这说明我们的第一步修复是正确的!Nginx 已经成功将请求转发给了 frps。但 frps 却不认这个请求。

为什么呢?因为 frps 就像一个大厦的保安,它收到了一个指名道姓要找 xc-test.hlj.com 先生的访客(HTTP 请求),它翻遍了登记簿(已连接的 frpc 客户端列表),发现根本没有一个客户端叫这个名字,或者说没有一个客户端在 frpc.ini 里配置过 custom_domains = xc-test.hlj.com

保安大哥只能两手一摊:"查无此人,慢走不送!"

终极骚操作:Nginx 的"偷天换日"

到这里,最"正统"的解决方案,是去修改内网的 frpc.ini 文件,加上新的域名。

ini 复制代码
# File: frpc.ini (内网客户端)
[my_proxy]
type = http
...
custom_domains = xc.f.wmeimob.com,xc-test.hlj.com

但是这也太麻烦了!那么:有没有办法不动客户端,只在公网服务器上解决问题?毕竟修改的地方也太多了。只是项目开发临时使用,结束后就停掉了,而且把所有复杂性都收敛在网关层,才是优雅的架构之道嘛!

答案是:当然可以! 核心武器就是 Nginx 的 proxy_set_header 指令。

我们可以在 Nginx 层给请求"化个妆",当它访问 frps 的时候,让 frps 以为它就是自己认识的那个"老朋友"。

终极解决方案:

  1. 为新域名申请一个 SSL 证书 。HTTPS 时代,没有证书寸步难行。这里我隆重推荐 Let's Encrypt + Certbot,免费、自动、省心!

    bash 复制代码
    # 一行命令,自动申请并配置Nginx
    sudo certbot --nginx -d xc-test.hlj.com
  2. 为新域名创建一个专属的 Nginx 配置,并施展"偷天换日"大法。

    nginx 复制代码
    server {
        listen 443 ssl;
        server_name xc-test.hlj.com;
    
        # 1. 使用 Certbot 生成的证书
        ssl_certificate /etc/letsencrypt/live/fullchain.pem);
        ssl_certificate_key /etc/letsencrypt/live/xxx.pem;
        include /etc/letsencrypt/options-ssl-nginx.conf;
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
    
        location / {
            # 2. 核心:在转发前,把Host头换成frp认识的那个
            proxy_set_header Host lishuwei-xcheng.f.wmeimob.com;
    
            # 别忘了把其他必要的头信息也带上
            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. 转发给本地的frps服务
            proxy_pass http://127.0.0.1:6666;
        }
    }

nginx -s reload,再次刷新页面,熟悉的开发环境界面终于出现了!完美!🎉

最终的请求链路图:

graph TD; subgraph "公网服务器" D["Nginx"] E["FRP服务端(frps)"] end subgraph "内网环境" F["FRP客户端(frpc)"] G["开发环境服务"] end A["用户浏览器"] -->|"1. 请求 Nginx 公网 IP"| D D -->|"2. 根据 server_name 匹配
并反向代理到 FRP 服务"| E E -->|"3. 根据 subdomain 匹配
并转发流量"| F F -->|"4. 访问内网服务"| G

技术要点总结 🧠

这次排查虽然曲折,但复盘一下,涉及到的都是非常核心且实用的网络知识点,值得我们深入理解:

  1. CNAME 记录 :它只是 DNS 层面的一个"别名",并不会改变 浏览器发起的 HTTP 请求中的 Host 头。这是许多误解的根源。

  2. HTTP Host 头 :在虚拟主机技术普及的今天,Host 头是 Web 服务器(如 Nginx)用来区分同一个 IP 地址下不同网站的唯一标识

  3. Nginx server_name 匹配与默认服务器 :一定要理解 Nginx 是如何为请求选择 server 块的。当没有精确匹配时,默认服务器的行为可能会导致一些意想不到的"灵异事件"。在生产环境中,建议为 80 和 443 端口显式配置一个默认服务器,用于返回错误或默认页面,避免请求"漂移"。

  4. FRP 工作原理 :FRP 服务端(frps)是通过客户端(frpc)上报的 subdomaincustom_domains 信息来作为路由依据的。frps 本身就像一个基于域名的智能路由器。

  5. 反向代理的核心能力 proxy_set_header:它赋予了网关"修改"和"伪装"上游请求的能力,是解决类似域名不统一、协议转换、添加认证信息等问题的神器。

  6. HTTPS 与 SSL 证书:一个萝卜一个坑。一个域名必须对应一张有效的 SSL 证书,浏览器才会信任。好在有 Let's Encrypt 这样的免费 DV 证书颁发机构,让 HTTPS 的普及变得毫无障碍。

这次的排查实录希望能对大家有所启发。在复杂的网络世界里,每一个奇怪问题的背后,都隐藏着一连串扎实的基础知识。下次再遇到类似问题,希望你也能像侦探一样,抽丝剥茧,直捣黄龙!

相关推荐
喜欢你,还有大家1 分钟前
Linux笔记10——shell编程基础-4
linux·运维·服务器·笔记
不懂机器人7 分钟前
linux编程----网络通信(TCP)
linux·服务器·tcp/ip
tanyongxi661 小时前
简易shell
linux·运维·服务器
vortex51 小时前
Python包管理与安装机制详解
linux·python·pip
zcz16071278211 小时前
CentOS 7 服务器初始化完整流程
linux·服务器·centos
Twinkle1751 小时前
linux下的网络编程:TCP(传输控制协议)编程
linux·网络·tcp/ip
Clain2 小时前
如何快速建站 | 云服务器配置+公网ip部署网站全流程详解
linux·运维·服务器
云动雨颤3 小时前
Linux下PXE服务器搭建
linux·运维·服务器
源远流长jerry3 小时前
STM32之DMA详解
linux·网络·c++·stm32·单片机·嵌入式硬件