Backend Development 7 min read

Root Cause Analysis of HTTP 400 Errors After Spring Boot and Tomcat Upgrade Due to Nginx Host Header Handling

The article investigates why upgrading Spring Boot (and Tomcat) caused massive HTTP 400 errors in production, traces the issue to Nginx forwarding a Host header with an underscore, explains Tomcat's stricter RFC‑compliant validation introduced in version 8.5.31, and provides reproducible steps and remediation guidance.

Java Captain
Java Captain
Java Captain
Root Cause Analysis of HTTP 400 Errors After Spring Boot and Tomcat Upgrade Due to Nginx Host Header Handling

To address distributed tracing, the team introduced Jaeger and built a SpringBoot starter that can be added with minimal changes to enable full‑link tracing.

After upgrading the internal SpringBoot framework (which also upgraded Tomcat from 8.5.11 to 8.5.31) and adding the starter, the production environment began returning a large number of HTTP 400 errors from Nginx.

Initial log checks (ELK and host logs) showed no obvious errors. Stress testing in a separate environment reproduced the 400 errors, and the problem was resolved by adding a single line to the Nginx location block:

proxy_set_header HOST $host

This directive forces Nginx to forward the original Host header (e.g., Host: abc.com ) instead of the upstream name, which is crucial because, without it, Nginx substitutes the upstream name as the Host header.

Further investigation revealed that the issue only occurs with the newer Tomcat version. When the upstream name contains an underscore (e.g., sc_java ), Tomcat 8.5.31 validates the Host header against RFC 1034 and rejects it, resulting in a 400 response. Tomcat 8.5.11 did not perform this validation, so the same configuration worked previously.

The team reproduced the problem locally by deploying two backend services on ports 8083 and 8084, configuring Nginx with an upstream name containing an underscore, and sending requests via Postman. The 400 error disappeared after either renaming the upstream to remove the underscore or adding the proxy_set_header HOST $host directive.

Root‑cause analysis of Tomcat source code showed that version 8.5.31 introduced a new parseHost method in AbstractProcessor that calls Host.parse(valueMB) , enforcing RFC‑compliant host validation. The change was committed on 2018‑04‑06.

Recommendations: avoid using underscores in Nginx upstream names and, when possible, add proxy_set_header HOST $host in the Nginx location block to ensure the original Host header is preserved.

In summary, the HTTP 400 errors were caused by Tomcat’s stricter Host‑header validation introduced in the newer version, not by the Jaeger starter itself.

backendDistributed TracingnginxSpringBootTomcatJaegerhttp-400
Java Captain
Written by

Java Captain

Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.