为什么你的 URL 需要 80 或 443 端口的入口,而不是 45000
浏览器按方案选择 80 或 443,而不是你服务商的高位端口。走一遍入口路径,以及每种发布方式留给你的来源。
在浏览器里输入主机名,端口由方案决定:http 是 80,https 是 443。DNS 应答里没有任何东西能推翻这一点,你机器上的任何转发规则也无法在服务商边缘凭空造出一个监听者。当 VPN 服务商给你一个像 45000 这样的高位公网端口时,带端口的 URL 能用,干净的 URL 不能用。缺口在入口处,不在应用内部。
把从浏览器到进程的路径走一遍,三种发布方式就不再显得神秘。
看请求实际去了哪里
浏览器把名字解析成地址,然后连接到该地址的方案默认端口,除非 URL 指定了别的端口。到达的连接会怎样,由你服务商的边缘决定。如果它的转发表只把 45000 映射到你的内部地址,那么发往 80 的连接就无处可去,在你的机器参与之前就被拒绝或丢弃。
这种拒绝发生在边缘,所以你的应用日志什么都看不到。没有日志是包在哪里停下的证据,而不是应用坏了的证据。
为什么本地规则修不了边缘
应用主机上的重定向或 DNAT 规则只作用于已经到达的包。发往服务商 80 端口的包根本到不了,所以规则永远不会被评估。这是整个问题中最常被浪费的一小时。
DNS 也带不了端口。HTTP 规范 定义了方案的默认值,A 或 AAAA 记录返回地址,而 CNAME 返回另一个名字。浏览器仍然会拼接方案的端口,所以只有 URL 里显式的端口才能改变目的地。
选项一:保留显式端口
通常正确的答案是不起眼的那个。发布带端口的完整 URL,在支持它的入口上终止 TLS,并确保每个生成的链接都保持同一个来源。对于小而可控的受众,这已经完整,除了一个看得见的端口号之外没有别的代价。
顺手检查重定向和 cookie 作用域,因为把自身主机名写进 Location 头的后端会毁掉这套安排。
选项二:拿到真正的默认端口入口
有些服务商和托管方案会把共享或独立地址上的 80 和 443 作为有文档记录的服务转发。问清楚实际包含什么,而不是假设一个公网出口地址就意味着任意入站端口。独立出口地址并不说明入站转发。
以书面形式确认归属和可用性,包括隧道用另一个地址重连时入口会怎样。随隧道移动的入口会造成看起来像 DNS 故障的中断。
选项三:在前面放一个前端
可达地址上的反向代理可以 在 443 上监听并终止 TLS,并通过 VPN 单独连接后端。访问者看到的是前端的名字和默认端口,而前端代表你去和后端的高位端口通信。这之所以有效,是因为它改变的是架构,而不是跟入口较劲。这样后端在公网上根本不需要入站端口。
它还改变了你要运维的东西。前端掌握公网 TLS、访问策略、日志和可用性,而且必须把后端选择限制在预期服务上。如果从前端到后端的这一跳使用 HTTPS,就在这一跳上保留证书校验。
保持单一来源
前端的公网主机名、方案和端口构成访问者使用的来源,所以按这个来源配置应用的外部 URL、重定向和 cookie。如果应用要生成绝对链接,就传原始主机和转发方案。把用户弹回私有名称或高位端口的响应,会毁掉你刚建好的干净入口。
然后带着受众去决定。短 URL 可能值得一个受管前端,但不值得不小心把只给 VPN 用的私有服务变成公开服务。