一次因企微域名限制引发的“骚操作”: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 的普及变得毫无障碍。

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

相关推荐
轻松Ai享生活21 小时前
5 节课深入学习Linux Cgroups
linux
christine-rr21 小时前
linux常用命令(4)——压缩命令
linux·服务器·redis
三坛海会大神5551 天前
LVS与Keepalived详解(二)LVS负载均衡实现实操
linux·负载均衡·lvs
東雪蓮☆1 天前
深入理解 LVS-DR 模式与 Keepalived 高可用集群
linux·运维·服务器·lvs
乌萨奇也要立志学C++1 天前
【Linux】进程概念(二):进程查看与 fork 初探
linux·运维·服务器
雨落Liy1 天前
Nginx 从入门到进阶:反向代理、负载均衡与高性能实战指南
运维·nginx·负载均衡
Yyyy4821 天前
Nginx负载均衡集群实验步骤
运维·nginx·负载均衡
獭.獭.1 天前
Linux -- 信号【上】
linux·运维·服务器
hashiqimiya1 天前
centos配置环境变量jdk
linux·运维·centos
hashiqimiya1 天前
权限更改centos中系统文件无法创建文件夹,使用命令让普通用户具备操作文件夹
linux