Nginx报“upstream sent invalid header”错误,表明后端返回的HTTP响应头含非法字符(如控制符、空格、Unicode),需重点排查手动拼接header处、直连抓包验证、禁用中间件定位污染源。

看到 upstream sent invalid header 这条错误,说明 Nginx 已成功完成 TLS 握手、转发请求、收到响应,但在解析后端返回的 HTTP 响应头时直接拒收——问题不在网络或证书,而在后端输出的 header 字节流本身不合法。
要快速锁定漏洞路径,关键不是“查所有代码”,而是聚焦三个可验证、可复现、有日志锚点的环节。
重点排查 header 拼接与注入逻辑
后端框架中手动构造 header 的地方最危险,尤其是值来自变量拼接且未做清洗:
- Spring Boot 中
response.setHeader("Cache-Control", "public, max-age=" + ttl):若ttl为空或为null,结果变成"public, max-age=",末尾多出等号虽不致命,但若拼接"no-cache "(结尾带空格)就触发Cache报错 - Servlet Filter 或拦截器里调用
httpServletResponse.addHeader("X-Trace-ID", id + "-v1"):当id为空字符串,结果为"-v1",以破折号开头,部分 HTTP 解析器(包括 Nginx)会拒绝该字段名 - 自定义 Header 工具类中使用
String.format("value: %s", rawValue),而rawValue含u00A0(不间断空格)或换行符,导致 header 值跨行或含非法 Unicode
建议立即检查所有 setHeader、addHeader、header()(如 Spring WebFlux)、response.headers().set()(如 Vert.x)调用点,重点看是否:
- 直接拼接用户输入、配置项、数据库字段
- 未对值做
.trim()和正则过滤(如replaceAll("[\p{Cntrl}\su00A0]+", "")) - 在空值、null、空白字符串场景下产生非法前缀或后缀
快速验证哪条接口路径在吐异常 header
别靠猜,用 Nginx 日志字段直接定位:
在对应 location 块中临时添加:
log_format header_debug '$remote_addr [$time_local] "$request" '
'$status $upstream_http_cache_control $upstream_http_etag '
'"$upstream_http_x_trace_id" "$upstream_http_content_type"';
access_log /var/log/nginx/header_debug.log header_debug;然后观察日志中 $upstream_http_cache_control 是否出现:
- 完全为空(说明 header 被 Nginx 解析失败,压根没提取到)
- 显示为
Cache public或max-age 3600(表示空格,直接暴露非法字符位置) - 出现乱码字节如
(即u00A0)或
