Tomcat升级至9.0.29后因仅传入冒号作为Host启动失败
我来帮你拆解这个问题——你把Tomcat从8.5.24升级到9.0.29后,当请求的Host参数只传了一个冒号:时,Tomcat触发非法参数异常并启动失败,对吧?这其实是Tomcat版本升级后校验逻辑收紧导致的问题,下面给你详细分析和解决方案:
问题根源
Tomcat 9.x系列对HTTP请求的Host头校验做了严格升级,完全遵循HTTP RFC规范。在org.apache.tomcat.util.http.parser.Host类的parse方法(对应报错里的Host.java:78)中,新增了更严谨的格式校验:合法的Host应该是域名/IP,或者带端口的域名:端口/IP:端口格式,单纯的冒号:完全不符合规范,因此直接抛出IllegalArgumentException。而你之前用的8.5.24版本校验逻辑相对宽松,允许了这种不符合规范的输入。
可行的解决方案(按推荐程度排序)
优先方案:在请求入口拦截非法请求
最安全且符合规范的做法是在前端客户端、负载均衡或反向代理层(如Nginx、Apache HTTP Server)添加校验规则,直接拦截Host头仅为冒号的请求。以Nginx为例,你可以在配置里添加:server { # 其他原有配置... if ($host = ':') { return 400 Bad Request; } }这样非法请求根本到不了Tomcat,从源头避免了错误。
临时应急:调整Tomcat日志级别(仅屏蔽日志,不解决根源)
如果暂时无法拦截请求,你可以修改Tomcat的conf/logging.properties,把org.apache.coyote.AbstractProcessor的日志级别设为FINE(对应DEBUG级别),这样后续的解析错误只会在DEBUG日志里记录,不会影响Tomcat正常运行——不过这只是掩盖问题,不是真正解决,不推荐长期使用。org.apache.coyote.AbstractProcessor.level = FINE不推荐:修改Tomcat源码调整校验逻辑
如果你必须允许这种非法请求,可以修改Host.java里的parse方法,放宽对Host格式的校验。但这么做会破坏HTTP规范,而且后续Tomcat升级时需要重新修改代码,维护成本很高,除非万不得已不建议这么做。临时降级:回退到Tomcat 8.5.24
如果业务暂时无法调整,且必须支持这种不符合规范的请求,可以临时回退到原来的8.5.24版本,但这只是权宜之计,长远来看还是要修复请求的合法性或者在入口拦截。
附你提供的报错信息
org.apache.coyote.AbstractProcessor.parseHost
The host [:] is not valid
Note: further occurrences of request parsing errors will be logged at DEBUG level.
java.lang.IllegalArgumentException
at org.apache.tomcat.util.http.parser.Host.parse(Host.java:78)
at org.apache.tomcat.util.http.parser.Host.parse(Host.java:45)
at org.apache.coyote.AbstractProcessor.parseHost(AbstractProcessor.java:294)
at org.apache.coyote.http11.Http11Processor.prepareRequest(Http11Processor.java:759)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:343)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1591)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
内容的提问来源于stack exchange,提问作者aatish bansal

