Nginx port_in_redirect
我是在实践 Cloudflare Tunnel 注意到一个奇怪的现象:
当我的页面中嵌入的URL没有跟随 /
,也就是使用了 https://docs.cloud-atlas.dev/discovery
而不是 https://docs.cloud-atlas.dev/discovery/
。此时作为 Cloudflare Tunnel 后端的NGINX运行在 8001
端口,浏览器客户端会访问 https://docs.cloud-atlas.dev:8001/discovery/
。这导致根本打不开页面,因为Cloudfare Tunnel对外输出的HTTPS端口是443,客户端是无法访问内部服务器端口 80001
的。
为什么会出现这么奇怪的将内部服务器端口暴露到Internet的现象?
NGINX如何处理url最后没有加 /
简单来说:
在URL中,最后的
/
和我们UNIX文件系统类似,表示的是一个路径(目录),此时会按照NGINX配置index index.html index.htm;
去自动加载index.html
页面如果URL最后没有加上
/
,那么NGINX/Apache等WEB服务器就会按照如下顺序去判断:首先假设最后没有
/
的URL部分是一个文件,例如https://docs.cloud-atlas.dev/discovery
就假设在WEB服务器根目录下可能有一个名为discovery
的文件,先尝试去访问discovery
文件来提供给浏览器客户端当然这里确实没有这个
discovery
文件,那么WEB服务器就会默认将该URL理解为对一个目录的请求,也就是discovery/
目录的请求重定向行为 : WEB服务器通常会执行
301
永久重定向,将请求自动转跳到discovery/
。这样,用户访问的就是该目录下的默认文件(通常是index.html
)
为什么会暴露出 8001
端口
这就涉及到NGINX的一个默认配置 port_in_redirect
:
Module ngx_http_core_module >> port_in_redirect 默认设置是
on
:当NGINX重定向一个URL时候,默认会将服务器端口添加到重定向URL中: 对于我的案例就是
301
永久重定向,自动转跳discovery/
时会插入8001
端口
为什么以前部署反向代理没有暴露后端服务器端口
在没有使用 Cloudflare Tunnel 之前,我使用 NGINX反向代理https 来部署网站,关于反向代理的配置设置,有一段 proxy_set_header
配置修正了这个问题(这里取 Nginx配置文件的include 配置为例):
/etc/nginx/include/proxy/proxy_set.conf
proxy_http_version 1.1;
proxy_cache_bypass $http_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header Connection "";
这个 proxy_set_header X-Forwarded-Port $server_port;
表示:
Nginx反向代理服务器 在端口 443 上接收请求,添加标头
X-Forwarded-Port: 443
($server_port
),并将请求转发到端口8001
上的后端当后端WEB服务器生成重定向时,后端NGINX会使用header(443),并将客户端正确重定向到
https://docs.cloud-atlas.dev/discovery/
,而不是画蛇添注增加port_in_redirect
的8001
端口
结合 Cloudflare Tunnel 的修正方法
既然已经了解了前因后果,那么修正方法有:
方法一: 在后端NGINX服务器配置
port_in_redirect off;
以杜绝重定向暴露错误的端口号(我采用这个方法):
/etc/nginx/sites-available/docs.cloud-atlas.dev
配置NGINXserver {
listen 8001;
listen [::]:8001;
server_name docs.cloud-atlas.dev;
port_in_redirect off;
root /docs/www/docs.cloud-atlas.dev;
index index.html;
location / {
try_files $uri $uri/ =404;
}
location /discovery/ {
alias /docs/www/discovery/;
}
}
方法二: 修订 Cloudflare Tunnel 配置,使用
httpHostHeader
设置来明确发送正确的公开主机名端口方法三: 在 Cloudflare dashboard 的
Rules > Redirect Rules
设置一个处理 trailing slash 的URL规则:Forwarding URL with a 301 (Permanent Redirect)
的动作包含trailing slash
警告
上述方法二和三我没有实践,仅参考Google搜索AI回答记录参考
301 (Permanent Redirect)
问题
我发现一个尴尬的问题,就是之前错误的 port_in_redirect
默认配置对于客户端是有持久影响的,如果客户端不清除缓存,会一直保留着重定向URL中插入 8001
端口。当然浏览器客户端清理缓存是能够解决问题的,但实际是不可能让用户清理换缓存但。
我理解是需要让nginx发送给客户端页面内容已经更新,强制客户端刷新缓存。不过,目前是实验环境,就我一个人使用,我还没哟研究。