问题现象

我的博客部署在内网机器上,通过 Cloudflare Tunnel 进行内网穿透,将内网地址 172.22.26.185:50001 映射到了外网域名 tianlejin.top

在测试访问时,有以下现象:

  1. 内网访问正常:在内网直接访问 http://172.22.26.185:50001/blog/Linux_Clash/http://172.22.26.185:50001/blog/Linux_Clash(不带末尾斜杠),都能正常打开页面。
  2. 外网带斜杠访问正常:通过外网域名访问 https://tianlejin.top/blog/Linux_Clash/,页面秒开。
  3. 外网不带斜杠访问异常:通过外网域名访问 https://tianlejin.top/blog/Linux_Clash(不带末尾斜杠),浏览器会卡住很久,然后地址栏变成了 http://tianlejin.top:50001/blog/Linux_Clash/,最终页面显示“tianlejin.top 目前无法处理此请求。HTTP ERROR 502”。

原因分析

这个问题本质上是 Nginx 的目录重定向机制和 Cloudflare 的端口限制共同作用的结果。

1. Nginx 的目录重定向(Trailing Slash Redirect)

当我们在浏览器中访问 https://tianlejin.top/blog/Linux_Clash(末尾没有斜杠)时,Nginx 收到请求后,会在服务器的硬盘上查找这个路径。

Nginx 发现 Linux_Clash 实际上是一个目录(文件夹),而不是一个具体的文件(比如 .html 文件)。为了确保这个目录下的相对路径资源(比如图片、CSS 文件)能够被浏览器正确解析,Nginx 会自动返回一个 301 永久重定向(Moved Permanently) 响应,要求浏览器去访问带斜杠的地址(/blog/Linux_Clash/)。

2. 端口泄露(Port Leakage)

在返回 301 重定向时,Nginx 需要在响应头的 Location 字段中告诉浏览器新的地址。默认情况下,Nginx 拼接这个新地址的规则是:

协议 + 服务器名称 + Nginx 监听的端口号 + 请求的 URI + /

在我的架构中,请求链路是:
外网用户 -> Cloudflare (443端口) -> 内网 Nginx (50001端口)

因为 Nginx 是在本地监听 50001 端口,且位于 Cloudflare Tunnel 之后,它并不知道外网用户是通过 HTTPS (443端口) 访问的。所以,它拼接出来的重定向地址变成了:

http://tianlejin.top:50001/blog/Linux_Clash/

3. Cloudflare 的端口限制导致 502

浏览器收到 Nginx 返回的 301 重定向响应后,会尝试去请求新的地址:http://tianlejin.top:50001/blog/Linux_Clash/

但是,tianlejin.top 的 DNS 解析是指向 Cloudflare 的。Cloudflare 默认只代理标准的 Web 端口(如 80, 443 等),外网根本无法访问 50001 端口

因此,浏览器发出的请求一直等不到回应,卡了很久之后最终超时,报出 502 错误。


解决方法

最根本的解决方法是告诉 Nginx:在进行重定向时,不要加上本地的端口号

这可以通过修改 Nginx 的 port_in_redirect 指令来实现。

  1. 打开 Nginx 的站点配置文件(我使用的是宝塔面板,可以在“网站” -> “设置” -> “配置文件”中找到)。
  2. 在对应的 server { ... } 块中,添加一行 port_in_redirect off;

修改后的配置大概如下:

1
2
3
4
5
6
7
8
9
server {
listen 50001;
server_name tianlejin.top;

# 添加这一行,禁止重定向时携带端口
port_in_redirect off;

# ... 其他配置保持不变 ...
}
  1. 保存配置并重载 Nginx:
1
sudo nginx -s reload

为什么这样就能解决?

port_in_redirect 的默认值是 on,即 Nginx 在生成重定向 URL 时会带上它自己监听的端口号。

将其设置为 off 后,Nginx 拼接重定向地址的规则变成了:

协议 + 服务器名称 + 请求的 URI + /

这样,Nginx 返回给浏览器的重定向地址就变成了 http://tianlejin.top/blog/Linux_Clash/(没有了 :50001)。浏览器收到这个新地址后,会去请求标准的 80 端口(随后 Cloudflare 会自动将其升级为 443 端口的 HTTPS),请求就能顺利通过 Cloudflare 到达内网服务器,页面也就正常打开了。


总结

在使用反向代理(如 Cloudflare Tunnel、FRP 等)将内网非标准端口映射到外网标准端口时,如果后端使用的是 Nginx,很容易遇到这种因为目录重定向导致的端口泄露问题。记住在 Nginx 配置中加上 port_in_redirect off;,可以避免很多不必要的麻烦。


作者:Copilot + Genimi 3.1 Pro