外网访问打不开?3种场景根治端口转发丢端口

简介 外网访问打不开?3种场景根治端口转发丢端口原创:凯哥Java本文标签:路由器端口转发丢端口、SpringBoot重定向端口、TomcatproxyPort配置前两天一兄弟在群里喊:凯哥,我这在路由器上做了端口转发,外网访问http://公网IP:17080/项目名死活打不开,浏览器直接报无法连接。他第一反应就是路由器坏了、转发没配好。我让他先别动路由器。这种「一访问就跳转到没端口的地址、然后打不开

🔔🔔好消息!好消息!🔔🔔

有需要的朋友👉:微信号 kaigejava2022

外网访问打不开?3种场景根治端口转发丢端口

原创:凯哥Java

本文标签:路由器端口转发丢端口、SpringBoot重定向端口、Tomcat proxyPort配置


前两天一兄弟在群里喊:凯哥,我这在路由器上做了端口转发,外网访问 http://公网IP:17080/项目名 死活打不开,浏览器直接报无法连接。他第一反应就是路由器坏了、转发没配好。

我让他先别动路由器。这种「一访问就跳转到没端口的地址、然后打不开」的现象,我见得太多了——锅大概率不在路由器,在你后端那个 Web 应用身上


摘要内容:外网访问端口转发地址打不开,常见原因是后端 Tomcat/SpringBoot 返回了不带公网端口的 301/302 重定向;路由器四层 NAT 不会改写 Location 头。本文给出 3 种场景的永久修复方案,含 SpringBoot、原生 Tomcat 与代码层写法。


封面图:路由器转发数据时,端口标识在重定向环节被"剥落"的示意




一、先说结论:锅不在路由器,在你后端应用


先把现象还原一遍,你就明白了。

你在外网访问:


http://1.2.3.4:17080/your-app   (末尾没加斜杠)


应用自己「啪」一下给你 301 跳转到:


http://1.2.3.4/your-app/        (:17080 没了)


浏览器拿到这个没有端口的地址,HTTP 默认走 80 端口,结果就是去敲 80——你内网服务又没监听 80,自然打不开。

🔥 重点来了:没有 Nginx、纯路由器端口转发的情况下,路由器本身不会主动去改 HTTP 的 Location 重定向头。端口之所以丢,100% 是你后端 Web 应用(Tomcat 或 SpringBoot 内置 Tomcat)自己吐了一个不带端口的 301/302。

想确认?在外网机器上敲一条命令看返回的 Location:


curl -I http://1.2.3.4:17080/your-app


你大概率会看到:


HTTP/1.1 301 Moved Permanently


Location: http://1.2.3.4/your-app/


下面这张是凯哥在外网机器上实际跑出来的响应头——Location 后面只跟了 IP,根本没带 17080,再点下去就会去打默认 80 端口:

实拍:外网执行 curl -I 拿到的 301 响应头,Location 端口已丢失


✅ 实锤:跳转 URL 里压根没有 17080。根因不在网络,在应用。



二、根子在哪:四层 NAT 不会改 Location 头


一句话讲透:路由器做的端口转发,本质是四层(TCP)NAT,它只管把「外网某个端口的包」转给「内网某台机器的某个端口」,根本不解析、也不改写上层的 HTTP 报文。

你的链路自外而内只有三跳


外网客户端  →  路由器 17080(公网入口)


▼  端口转发

             内网服务器 8080(真实监听端口)


路由器只做包级别的转发:把「公网 17080 收到的包」原样丢给「内网服务器 8080」。它不解析 HTTP,也不重写 Host/Location。所以到了内网服务器这一步,它只知道自己当时监听的是 8080,完全不晓得外面进来的用户敲的是 17080。

图2:外网 17080 进来,到内网变成了 8080——这就是「丢端口」的源头


结果就是:Tomcat/SpringBoot 在生成「目录补斜杠」这类绝对跳转地址时,只拿自己监听的 8080 拼,完全不带上公网的 17080——拼出来的 Location 自然把公网端口丢了。

⚠️ 这点一定要分清:路由器端口映射 ≠ 反向代理。四层 NAT 没法把「外部访问端口」告诉后端程序;只有七层代理(Nginx/Apache)才能附加 X-Forwarded-Port、改写 Host 头,让后端知道「用户是从哪个公网端口进来的」。所以别再对着路由器一顿瞎调了。

图3:客户端经路由器端口转发到内网服务,端口标识在重定向环节脱落




三、3 种场景永久修复(含临时规避)


💡 先给一个立刻能用、不用改代码的临时方案:访问时地址末尾强制加上斜杠


http://1.2.3.4:17080/your-app/


带上 / 之后,很多 Web 容器不会触发「目录补斜杠」重定向,端口自然不丢。书签直接存这个完整地址就行。但这是治标,下面三个场景才是治本。


场景1:SpringBoot(最常见)


application.yml 加一行,让重定向走相对路径:


server:


tomcat:

   use-relative-redirects: true


作用:不再生成 http://ip/path/ 这种绝对 URL,跳转变为 /your-app/,浏览器自动沿用当前 IP + 端口,端口不会丢。老版本用 properties 写法:


server.tomcat.use-relative-redirects=true



场景2:原生 Tomcat(war 包部署)


conf/server.xml 里的 Connector,补一个 proxyPort写公网对外端口 17080,不是内网端口


<Connector port="8080" protocol="HTTP/1.1"


connectionTimeout="20000"

redirectPort="8443"

          proxyPort="17080" />


改完重启 Tomcat 即可。Tomcat 会拿 proxyPort 拼绝对地址,端口就回来了。


场景3:代码里手动 sendRedirect


千万别写死绝对地址。下面这种 ❌ 写法,公网一访问端口就丢:


// ❌ 绝对地址,端口会丢


response.sendRedirect("http://1.2.3.4/your-app/");


改成 ✅ 相对路径,框架自动带上当前上下文和端口:


// ✅ 相对路径,沿用当前 IP + 端口


response.sendRedirect("/your-app/");


🚨 凯哥的踩坑提醒:浏览器会永久缓存 301,改完配置一定要开无痕窗口测;另外确认路由器只是单纯的虚拟服务器/端口转发,别顺手开了 DMZ、防火墙 HTTP 劫持、强制门户那类功能。最稳的验证:内网直接用内网 IP + 内部端口访问,如果内网也跳丢端口,那就是应用本身行为;内网正常、只有外网异常,才是代理端口识别问题。

图4:改用相对重定向后,跳转沿用原端口,访问恢复正常




结束语


大家好,我是凯哥Java(kaigejava),乐于分享技术文章,欢迎大家关注"凯哥Java",及时了解更多。让我们一起学Java。也欢迎大家有事没事就来和凯哥聊聊~~~

如果你最近在搞路由器端口转发、SpringBoot 或 Tomcat 外网部署,遇到「一访问就跳转到没端口的地址」这种坑,先 curl -I 抓一眼 Location,照上面 3 种场景对症改一行配置,比对着路由器瞎调一天都管用。


路由器端口转发访问打不开排查

端口转发301重定向丢端口原因

SpringBoot use-relative-redirects 配置

Tomcat proxyPort 外网端口设置

sendRedirect 相对路径写法

路由器端口转发301跳转丢端口解决方案

Tomcat proxyPort公网端口配置实战

SpringBoot相对重定向外网部署指南


作者:凯哥Java

类型:原创

标签:路由器端口转发、SpringBoot重定向、Tomcat配置、端口丢失


原创声明:本文原创发表于「凯哥Java」,转载请注明出处。


TopTop